How can I call a view function inside of a liveview template?

I have this simple view in my views folder, but I’m having compilation errors.

defmodule ZZZWeb.ListingLive.ShowView do
  use ZZZWeb, :view

  def currency(amount) do
    money = Money.new(amount, :USD)
    Money.to_string(money)
  end
end

In my template:

<h2 class="text-3xl font-bold mb-1">
  <%= currency @listing.price %>
</h2>

Finally my LiveView module:

defmodule ZZZWeb.ListingLive.Show do
  use ZZZWeb, :live_view

  alias ZZZ.Listings

  @impl true
  def mount(_params, _session, socket) do
    {:ok, socket}
  end

  @impl true
  def handle_params(%{"id" => id}, _, socket) do
    {:noreply,
     socket
     |> assign(:page_title, page_title(socket.assigns.live_action))
     |> assign(:listing, Listings.get_listing!(id))}
  end

  defp page_title(:show), do: "Show Listing"
  defp page_title(:edit), do: "Edit Listing"
end

Here’s the compilation error:

Erlang/OTP 24 [erts-12.0.3] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:1]

Compiling 1 file (.ex)

== Compilation error in file lib/zzz_web/live/listing_live/show.ex ==
** (CompileError) lib/zzz_web/live/listing_live/show.html.leex:115: undefined function currency/1
    (elixir 1.12.2) src/elixir_locals.erl:114: anonymous fn/3 in :elixir_locals.ensure_no_undefined_local/3
    (stdlib 3.15.1) erl_eval.erl:685: :erl_eval.do_apply/6
2 Likes

This was confusing for me at the beginning too. I came to think of LiveView as a bundle of View and Controller all in one.

That currency function should be defined in the LiveView. There’s no View (or controller) acting with LiveView, it’s already there.

PS: it was extra confusing coming from years writing web apps in django, and I had to get my head around this split, just to find out that the cool new toy doesn’t work like that anymore. I never saw the benefit of splitting this logic, so, better for me :stuck_out_tongue:

2 Likes

Move the currency function out into a separate helpers module, and import it in your ZZZWeb module in your live_view block, like so:

lib/zzz_web/your_helpers.ex

defmodule ZZZWeb.YourHelpers do
  def currency do
    # ...
  end
end

lib/zzz_web.ex

def live_view do
  quote do
    ...
    import ZZZWeb.YourHelpers
  end 
end

And it’ll be accessible to your liveview.

If you want to share it (and all your other view helpers) across live and regular views, put the import statement inside defp view_helpers, and then add it to your live_view block like so:

def live_view do
  quote do 
    # ...
    unquote(view_helpers())
  end
end
6 Likes

CRISPY solution :poultry_leg: :fire:

1 Like