Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rendering dynamic component #24

Closed
andreasknoepfle opened this issue Jan 29, 2020 · 4 comments
Closed

Rendering dynamic component #24

andreasknoepfle opened this issue Jan 29, 2020 · 4 comments

Comments

@andreasknoepfle
Copy link

Hi 👋

Is there some way of rendering a component dynamically? Like if I had an array with a Button and a Link component and I want to render them in a for loop:

def render(assigns) 
  content = [Button, Link]
  ~H"""
  <div :for={{component <- content}}>
     ???
  </div>
  """
end

I tried already some things like rendering with live_component() like :

{{ live_component, @socket, component }}

That works on an older state, but errors with the current master: BadMapError: expected a map, got: nil in Surface.ContentHandler.init_contents/1.

Also trying to interpolate the component somehow does not work, but would probably be a nice syntax:

<{{component}} />

Do you have any plans for this feature already (or am I missing something).
Since this is easily possible in plain LiveView it would sad if we couldn't also do this with surface.
I also imagine having the interpolation on a html element level like in react would be handy:

<h{{level}}>A headline</h{{level}}>

Also I would like to thank you for creating this project since I am already a huge fan of the idea 💚.

I would also volunteer to look into this and maybe create a PR if you give me some directions :)

Cheers
Andi

@msaraiva
Copy link
Member

Hi @andreasknoepfle!

Is there some way of rendering a component dynamically?

Not yet, but this is definitely on the roadmap and there's no way we'll ship the first version without it.

The plan is to have a built-in <Dynamic> component that can handle those cases. Something that you could use like this:

def render(assigns) do
  props = %{label: "Ok", class: "button"}
  component = Button

  ~H""" 
  <Dynamic component={{ component }} props={{ props }}/>
  """
end

A first naive implementation that would probably work for the most simple cases (like stateless components without nested children) would look like:

  defmodule Dynamic do
    use Surface.Component

    property component, :module, required: true
    property props, :map, default: %{}

    def render(assigns) do
      props =
        assigns
        |> Map.get(:props)
        |> Map.merge(%{__surface__: %{groups: %{__default__: %{binding: false, size: 0}}}}) # Don't worry about this for now :)

      ~H"""
      {{ live_component(@socket, @component, props) }}
      """
    end
  end

I'm currently working on the new event API due to some breaking changes in LiveView so, unfortunately, I wasn't able to validate the above solution but feel free to give it a try if you want and let me know if it works :)

Cheers.

@andreasknoepfle
Copy link
Author

Hi @msaraiva 👋

Your proposed solution works like a charm 💚.
Thank you very much! I am already excited and looking forward for a first release of surface 💯

Cheers

@msaraiva msaraiva added this to the v0.1.0 milestone Feb 21, 2020
@msaraiva msaraiva modified the milestones: v0.1.0, v0.2.0 Aug 11, 2020
@msaraiva msaraiva removed this from the v0.2.0 milestone Feb 24, 2021
@elliottneilclark
Copy link

Did anything come of this? The above Dynamic solution doesn't seem to work with default props.

@msaraiva
Copy link
Member

msaraiva commented Sep 6, 2021

Implemented in #473.

@msaraiva msaraiva closed this as completed Sep 6, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants