Call a LiveView/Phoenix component dynamically

Imagine you have a core component for LiveView/Phoenix component, and it has some static component like icon

For example:

<Heroicons.inbox_stack class="w-6 h-6 mx-auto stroke-current" />

You want to load it in a loop something like this:

<.block :for={{id, title, icon_module} <- tailwind_settings} id={id} title={title}>
  <icon_module class="w-6 h-6 mx-auto stroke-current" />
</.block>

How can we implement it?

Thank you in advance.

I found Phoenix.LiveView.HTMLEngine.component, but I could not use with string name of Elixir module and function like: "Elixir.Heroicons.inbox_stack"

<.block :for={{id, title, module} <- tailwind_settings} id={id} title={title}>
  <%= Phoenix.LiveView.HTMLEngine.component(
    String.to_existing_atom(module),
    [class: "w-6 h-6 mx-auto stroke-current"],
    {__ENV__.module, __ENV__.function, __ENV__.file, __ENV__.line}
  ) %>
</.block>

This function needs 3 arity and I can’t use apply, so String.to_existing_atom(module) is just used for converting to module

Do you have any idea?
Thanks

I did like this, finally see it works:

<.block :for={{id, title, module} <- tailwind_settings} id={id} title={title}>
  <%= Phoenix.LiveView.HTMLEngine.component(
    Code.eval_string("&#{module}/1") |> elem(0),
    [class: "w-6 h-6 mx-auto stroke-current"],
    {__ENV__.module, __ENV__.function, __ENV__.file, __ENV__.line}
  ) %>
</.block>

If you have another way, please let me know
Thanks

Function.capture/3 is what you’re looking for.

1 Like

I tried this, I have full name of module and its function which is like this: "Heroicons.inbox_stack", but it needs to have module and function name separately.

I’d suggest changing the input then to e.g. provide the function name only. Or the module and function name separately.

1 Like

Yes, you’re right, I just have one question,

<.block :for={{id, title, module} <- tailwind_settings} id={id} title={title}>
  <%= Phoenix.LiveView.HTMLEngine.component(
    Code.eval_string("&#{module}/1") |> elem(0),
    [class: "w-6 h-6 mx-auto stroke-current"],
    {__ENV__.module, __ENV__.function, __ENV__.file, __ENV__.line}
  ) %>
</.block>

This code is working now, I have no problem. The module is hard-coded, and I do not get it from user input, with these situations I am going to face with security issue?

4 Likes

Hey all! It seems that HTMLEngine.component is no longer there… does anyone have another way of dynamically render a component?

Seems it got moved to Phoenix.LiveView.TagEngine: Open Tokenizer for customization by feliperenan · Pull Request #2482 · phoenixframework/phoenix_live_view · GitHub

3 Likes

You rock @LostKobrakai ! Thanks a lot! I need to find a “public” api to do this before it changes again.