LiveComponent as a macro, compilation error

Hi !
I’m trying to combine LiveComponent and Elixir macro. The ideas is to facilitate new LiveComponent creation, specifically the template part.

But I have a compilation errror I can’t wrap my head around. Help would be appreciated. I have tried to wrap the L sigil with a macro_expand to no success.

The error

== Compilation error in file lib/msim_web/live/worlds/world_A/account_list_selector.ex ==
** (CompileError) lib/msim_web/live/worlds/world_A/account_list_selector.ex:2: expected "assigns" to expand to an existing variable or be part of a match
    (elixir) expanding macro: Kernel.var!/1
    lib/msim_web/live/worlds/world_A/account_list_selector.ex:2: MsimWeb.LV.Worlds.World_A.AccountListSelector.render/1
    expanding macro: Phoenix.LiveView.Helpers.sigil_L/2
    lib/msim_web/live/worlds/world_A/account_list_selector.ex:2: MsimWeb.LV.Worlds.World_A.AccountListSelector.render/1
    (elixir) lib/kernel/parallel_compiler.ex:229: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/7

The Macro

defmodule MsimWeb.LV.Worlds.World_A.DataSelector do
  defmacro __using__(_config) do
    quote do
      use Phoenix.LiveComponent
      import Msim.Utils
      require Logger
      use Bunch

      def render(assigns) do
        Logger.debug("")

        ~L"""
        <div class="round_border" id="data-selector-<%= @id %>">
        <%= if @data_list do %>
            <h3><%= unquote(data_title_gen)%></h3>

            <%= live_component @socket, MsimWeb.LV.Tools.NavButtons, target: "#data-selector-#{
          @id
        }", nav_event: "nav-list", pos: @data_pos, max: @data_size, list_size: @list_size %>

            <%= live_component @socket, MsimWeb.LV.Tools.ListHead, pos: @data_pos, list: @data_list, db_size: @data_size do %>
              <%= unquote(header_gen()) %>
            <% end %>

            <%= live_component @socket, MsimWeb.LV.Tools.ListBody, list: @data_list do %>
              <%= unquote(body_gen()) %>
            <% end %>
        <% end %>
        </div>
        """
      end (...)

The caller

defmodule MsimWeb.LV.Worlds.World_A.AccountListSelector do
  use MsimWeb.LV.Worlds.World_A.DataSelector
end

Thank you

Did you find the answer to this problem? I’m hitting the same issue…

I found the answer today myself, you have to use def render(var!(assigns)) as function signature.

1 Like

In case your wondering why: Elixir macros are hygienic.

This is a good thing, as it ensures that a variable named x defined in your macro doesn’t accidentally overwrite the variable named x in the calling scope. Which would be a great way to shoot yourself in the foot.

You can “break out” of this by using var! which allows to explicitly reference a variable from the calling scope.

The ~L-sigil also uses var! to access the assings of the calling scope and as such you need to assign assigns in this calling scope, which is done as @dvic showed.

3 Likes

Sorry for not answering, I missed the forum notification. No, I did not find the solution, but I will try your solution !