How to write a function that can either take an argument or default to set value?

I’m trying to write a function that can either take an argument or default to set value.

  def live_view(opts \\ []) do
    layout = opts[:layout] || {AppWeb.Layouts, :app}
    container = opts[:container] || {:div, class: "flex min-h-svh"}
    quote do
      use Phoenix.LiveView,
        layout: unquote(layout),
        container: unquote(container)

      unquote(html_helpers())
    end
  end

I’ve tried this, but I get an error stating:

** (UndefinedFunctionError) function AppWeb.live_view/0 is undefined or private.

To answer your question, one approach can be the multi arity approach:

def live_view() do
  live_view([{AppWeb.Layouts, :app}, {:div, class: "flex min-h-svh"}])
end

def live_view(opts) do
    layout = opts[:layout]
    container = opts[:container]
    quote do
      use Phoenix.LiveView,
        layout: unquote(layout),
        container: unquote(container)

      unquote(html_helpers())
    end
end

Maybe you can share your goal also?

To be able to help I think we’d need to see where you are calling your function from - not just where it’s defined.

From my reading of the code (returning AST from function, rather than a macro), you’re intending this function to be used at compile time. The error may well result from the module AppWeb not being compiled at the time you’re trying to call a function on it.

live_view is called from

defmodule AppWeb.UserLoginLive do
  use AppWeb, :live_view
end

I’m not quite sure how I’m going to pass the arguments, my intention was for the function to be called the same way as Phoenix.LiveView, inside the live.

Not a direct answer, but you can customize the layout from the mount callback:

def mount(_, _, socket) do
  {:ok, socket, layout: …}
end

No need to do that via the use Phoenix.LiveView call.

4 Likes