How to define a new function in all live_components with the macro __using__

What i did

I have a module called MyAppWeb.TestComponent that is defined like this

use MyAppWeb, :live_component

def test_component(assigns) do
   ~H"""
   <.live_component module={__MODULE__} {assigns} />
   """
end

def render(assigns) do
  ~H"""
  <div phx-click="test_event">Test</div>
  """
end

... other stuff

I want the function test_component do be defined automatically in the MyAppWeb module

This is what i did

defmacro __using__(:live_component) do
    function_name =
      __CALLER__.module
      |> Atom.to_string()
      |> String.split(".")
      |> List.last()
      |> Macro.underscore()
      |> String.to_atom()

    quote location: :keep do
      use Phoenix.LiveComponent

      unquote(helpers_verified_routes())
      unquote(helpers_html())
      unquote(helpers_elements())

      import Thimble.Phoenix.LiveHelpers

      def unquote(function_name)(assigns) do
        unquote(~H"""
        <.live_component module={#{__CALLER__.module}} {#{assigns}} />
        """)
      end
    end
end

The problems

I have two main problems:

  • sigil_H/2 is not defined
  • the last assigns is not defined

Any ideas of how can i make this work? This would come in handy to use live_components with the same syntax as the normal components allowing for better consistency

I managed to make this work

      def unquote(function_name)(assigns) do
        assigns = Map.put(assigns, :module, unquote(caller.module))

        Phoenix.Component.live_component(assigns)
      end
1 Like

You can move the sigil call out of the unquote and escape the assign interpolation.

def unquote(function_name)(assigns) do
  sigil_H(unquote("""
  <.live_component module={#{__CALLER__.module}} {\#{assigns}} />
  """))
end
1 Like

Unfortunately that doesn’t work.

undefined function sigil_H/1

I also tried putting import Phoenix.Component before that but it still gives the same error

Ah yeah, sigils are /2. You can provide an empty list of modifiers sigil_H(str, [])

1 Like

This should work:

def unquote(function_name)(var!(assigns)) do

More info: Kernel — Elixir v1.18.4

Yeah that makes sense thank you and sorry for the late reply!

P.S. I respect your pic