Can liveview UI be dynamically loaded using ~H?

Hi,

Normally when we write a Liveview UI, we use ~H sigil or provide a .heex file.
What I want to do is that I want to load some external content served as Liveview UI.

Such as ~r, it can be called as:

iex(3)> a = "b"
"b"
iex(4)> sigil_r("#{a}", 'i')
~r/b/i

Can I call the ~H sigil as a normal function by providing it a variable which contains the content?
I cannot figure out yet.

Thanks,
Ken Chen

1 Like

Uppercase sigils don’t allow interpolation.

1 Like

I looked into the source of sigil_H, it can be done by:

  def render(assigns) do
    template = """
    <li><%= @name %></li>
    """

    quoted = EEx.compile_string(template, [engine: Phoenix.LiveView.HTMLEngine])

    {result, _bindings} = Code.eval_quoted(quoted, assigns: assigns)
    result
  end

sigil_H source is this, but I just pass something seems necessary :grinning::

  defmacro sigil_H({:<<>>, meta, [expr]}, []) do
    options = [
      engine: Phoenix.LiveView.HTMLEngine,
      file: __CALLER__.file,
      line: __CALLER__.line + 1,
      module: __CALLER__.module,
      indentation: meta[:indentation] || 0
    ]

    EEx.compile_string(expr, options)
  end

You generally don’t want to compile templates at runtime. Compiling templates might be rather expensive. If you really need runtime templates I’d suggest benchmarking and possibly caching the compiled templates.

1 Like

Thanks for advice. I will see into caching it somehow.

Is the external content trusted? Keep in mind that HEEx is elixir code. If the external source is untrusted, or can be compromised, someone can run arbitrary Elixir code inside your application. I would consider something like mustache templates for external resources.

2 Likes

Yes. Would keep that in mind. The dynamic content is loaded from blockchain and hopefully it should not be changed. :grinning:

Not changing is good, but is it also trusted? That is to say, who can write to the block chain?

1 Like

This is something that used to be possible before with the ~E sigil which returns a Safe HTML representation, but I don’t think it’s possible to call function components programmatically like that; at least it seems it is heavily discouraged, see this thread for more information: Unexpected error while invoking assign/3 inside function component · Issue #1896 · phoenixframework/phoenix_live_view · GitHub. It might be better to generate valid HTML by hand and render it afterward.

1 Like

note that the component/2 function exists if you have a need to call a function component directly outside of a HEEx context

2 Likes

Are you talking about the Phoenix.LiveView.Helpers.component/2 function or the macro helper render_component/2 that you mentioned in the thread I linked?

If it’s the first one, some clarification in the docs might be needed to make it a bit more explicit because (I for instance didn’t even remember this function existed :sweat_smile:):

Renders a component defined by the given function.
This function is rarely invoked directly by users. Instead, it is used by ~H to render Phoenix.Components.

1 Like

@chrismccord If my template now would like to call a live_component, what should I do?

template = """
      <h1>Hello @name.  Please login your wallet.</h1>

      <div>
        <.live_component module={FunctionServerBasedOnArweaveWeb.Web3AccountComponent} id="web3account" />
      </div>
    """

My previous approach using EEx.compile_string and Code.eval_quoted encounters error:

nofile:4: undefined function live_component/1 (there is no such import)

I’m writing this to easily find it later if I need a solution in the future. :sweat_smile:

To dynamically load a partial based on a variable, use this code:

<%= Phoenix.LiveView.TagEngine.component(
  &apply(YourApp.LiveView, String.to_existing_atom("_#{@variable}"), [&1]),
  [],
  {__ENV__.module, __ENV__.function, __ENV__.file, __ENV__.line}
) %>

I’m not sure if this is encouraged due to the reasons mentioned in this thread, but it still works.

1 Like