Wrapping live_component that takes a block

I’ve been making use of live_component's ability to take a block in order to create a re-usable layout between live components.

<%= live_component(@socket, LayoutComponent, user: user, org: org) do %>
  <div>some content</div>
<% end %>

I would love to wrap this in a function like so:

<%= render_layout(assigns) do %>
  <div>some content</div>
<% end %>

… but I can’t quite get my head around how.

def render_layout(assigns) do
  live_component(
    assigns.socket,
    LayoutComponent,
    user: assigns.user,
    org: assigns.org
) do
  # ... uhhhhh, wait... uhhhhh... how do I capture the inner content?
end

I’m also wondering, would this would have an effect on diff-tracking? I know I can’t just use fn for this reason.
I also know custom functions affect this yet the generated code from phx.gen.live gives us a live_modal helper, so that’s why I was hoping this is possible.

Thanks!

The simplest way of wrapping is likely a macro here. This way change tracking shouldn’t be affected.

defmacro render_layout(do: block) do
  quote do
    live_component(@socket, LayoutComponent, [user: @user, org: @org], do: unquote(block))
  end
end
1 Like

Awesome, thank you!

I had to make a modification to get it to work:

defmacro render_layout(assigns, do: block) do
  quote do
    assigns = unquote(assigns)

    live_component(
      assigns.socket,
      LayoutComponent,
      user: assigns.user,
      org: assigns.org) do
        unquote(block)
      end
  end
end

I’m not super comfortable with macros yet… I guess I would to use var! on the assigns in your example to make it work? I haven’t tried yet, though I do prefer explicitly passing the assigns.

Thanks again! :smiley:

1 Like

This might break change tracking this way:

Another pitfall of .leex templates is related to variables. Due to the scope of variables, LiveView has to disable change tracking whenever variables are used in the template, with the exception of variables introduced by Elixir basic case, for, and other block constructs. Therefore, you must avoid code like this in your LiveEEx:

https://hexdocs.pm/phoenix_live_view/assigns-eex.html#liveeex-pitfalls

Yeah, that’s the part of the docs that worried me, I just don’t know why it’s ok to use live_modal then?

I also spoke too soon—my version doesn’t work on LiveViews containing components.

I think I need to re-think how I’m doing layout regardless. I’d like to open a topic on it but I’m still gathering info.