Basic question on passing controller functions to heex templates in Phoenix

My App is mostly a single Liveview page that contains all Heex and Elixir code on this single page.

However, their is a non Liveview page that I generated using the generators. This generated code breaks up the app into smaller pieces. The pieces I am focused on for this question are status_action_controller.ex and it’s corresponding index.html.heex file

My question is very simple.

If I create a function in status_action_controller.ex and I want to reference it and apply it to data in the index.html.heex template, how do I do this?

In the example below I want to simply take a function that does some time format conversion and apply it directly in the template. I’ve done this in my LiveView and it’s easy.

status_action_controller.ex

defmodule AppWeb.StatusActionController do
  use AppWeb, :controller

  alias App.StatusActions
  alias App.StatusActions.StatusAction

  def index(conn, _params) do
    status_actions = StatusActions.list_status_actions()
    render(conn, :index, status_actions: status_actions)
  end

  # _________________________________BEGIN___Convert time to human readable central time format

  def convert_time(time_stamp) do
    time_stamp
    |> DateTime.from_naive!("Europe/London")
    |> DateTime.shift_zone!("America/Montreal")
    |> Calendar.strftime("%m-%d-%y %I:%M %p")
  end

  # _________________________________END_____Convert time to human readable central time format

  # Below here is all CRUD functions .....
  
end

Index.html.heex

<body>
  <.header>
    Listing Status actions
  </.header>

  <.table
    id="status_actions"
    rows={@status_actions}
    row_click={&JS.navigate(~p"/status_actions/#{&1}")}
  >
    <:col :let={status_action} label="Testbed name"><%= status_action.testbed_name %></:col>
    <:col :let={status_action} label="Developer"><%= status_action.developer %></:col>
    <:col :let={status_action} label="Status"><%= status_action.status %></:col>
    <:col :let={status_action} label="Testbed value"><%= status_action.testbed_value %></:col>

    <!-- Below is what I want to do ... apply the convert_time function to time -->

    <:col :let={status_action} label="Date"><%= convert_time(status_action.updated_at ) %></:col>
  </.table>
</body>

What version of Phoenix are you on?

With 1.7 and above, if you want to call a function during the render of a template, you will need to place it in the *_html.ex corresponding to the controller.

So, in this case, you should see a status_action_html.ex. You can move your convert_time function there, and your index.heex.html should be able to access it.

Is there something wrong in my syntax above, bcause when I do that I get error:

" lib/app_web/controllers/status_action_html/index.html.heex:20: undefined function convert_time/1 (expected AppWeb.StatusActionHTML to define such a function or for it to be imported, but none are available)"

EDIT

This works:

status_action_html.ex

defmodule AppWeb.StatusActionHTML do
  use AppWeb, :html

  embed_templates "status_action_html/*"

  @doc """
  Renders a status_action form.
  """
  attr :changeset, Ecto.Changeset, required: true
  attr :action, :string, required: true

  def status_action_form(assigns)

  def convert_time(time_stamp) do
    time_stamp
    |> DateTime.from_naive!("Europe/London")
    |> DateTime.shift_zone!("America/Montreal")
    |> Calendar.strftime("%m-%d-%y %I:%M %p")
  end
end

Index.html.heex

<body>
  <.header>
    Listing Status actions
  </.header>

  <.table
    id="status_actions"
    rows={@status_actions}
    row_click={&JS.navigate(~p"/status_actions/#{&1}")}
  >
    <:col :let={status_action} label="Testbed name"><%= status_action.testbed_name %></:col>
    <:col :let={status_action} label="Developer"><%= status_action.developer %></:col>
    <:col :let={status_action} label="Status"><%= status_action.status %></:col>
    <:col :let={status_action} label="Testbed value"><%= status_action.testbed_value %></:col>

    <!-- Below is what I want to do ... apply the convert_time function to time -->

    <:col :let={status_action} label="Date"><%= convert_time(status_action.updated_at ) %></:col>
  </.table>
</body>