Are live components ever getting props?

I love the explicit syntax with attr for function components. Are we ever going to get declarative assigns like this for live components? That would be sweeeeeeet :grin:.

1 Like

Iā€™m not sure but I always make a ā€œconstructorā€ function component:

defmodule MyAppWeb.FooComponent do
  use MyAppWeb, :live_component

  attr :id, :string, required: true
  attr :foo, :string
  attr :bar, :string
  def live_foo(assigns) do
    ~H"""
    <.live_component
      module={__MODULE__}
      id={@id}
      foo={@foo}
      bar={@bar}
    />
    """
  end
end

Not does this let you use declarative assigns, it cleans up the callsites. I always prefix with live_ so itā€™s obvious they are live components but itā€™s not necessary.

I know Iā€™m not the only one who does this!

4 Likes

Interesting idea, suppose you could also make this into a macro as well as splat the assigns out. Any downsides though?

I believe that would break change-tracking, though there may be a way to make it work.

1 Like

According to documentation:

To interpolate a dynamic number of attributes in a keyword list or map, do:

<div title="My div" {@many_attributes}>
  <p>Hello <%= @username %></p>
</div>

It should be very easy to do so:

defmodule MyAppWeb.FooComponent do
  use MyAppWeb, :live_component

  props :props_assigns do
    attr :id, :string, required: true
    attr :foo, :string
    attr :bar, :string
  end

  def live_foo(assigns) do
    ~H"""
    <.live_component module={__MODULE__} {@props_assigns} />
    """
  end
end

Here is what needs to be done to make it work:

  1. Create props/2 macro which groups assigns using a specified key
  2. Create attr/3 macro which puts an desired attribute into current props (if any) and calls back LiveViewā€™s macro
  3. Create def/2 macro which collect assigns from props groups, removes them from original assigns, puts groups under a key specified in props/2 macro call and finally calls back LiveViewā€™s macro
1 Like

Oh, reassigning and splatting is a good idea!

I never have too many options for live components so I wouldnā€™t bother with a DSL like this myself, but if it became part of Phoenix Iā€™d use it.

This is unlikely to happen given live components do not get assigns just from the tag theyā€˜re rendered with, but also from send_update calls. For the tag based inputs a wrapper function component is imo the best solution.

This is how I do it to all my live components:

defmodule MyAppWeb.FooComponent do
  use MyAppWeb, :live_component

  attr :id, :string, required: true
  attr :foo, :string
  attr :bar, :string

  def live_render(assigns), do: ~H"<.live_component module={__MODULE__} {assigns} />"
end

Basically the same thing as the example @sodapopcan showed, but I pass the assigns directly so I donā€™t need to explicitly pass them all.

This will break change-tracking as far as I know, though.

1 Like

I donā€™t think it does, at least if it does I never noticed it in more than 300 components I have with it haha :sweat_smile:

Btw, this is how LiveDashboard handles live components too phoenix_live_dashboard/lib/phoenix/live_dashboard/page_builder.ex at d1578d877a99e0f31eb6384201f4bbd7239e4028 Ā· phoenixframework/phoenix_live_dashboard Ā· GitHub

1 Like

Hmmm ya, it doesnā€™t seem to and now I just got your second response, so interesting! Perhaps this applies to what is described near the bottom of this section. While it says you should never splat assigns, you can pass them into a function if you need to forward them along, so I suppose that is whatā€™s happening here and itā€™s ok because there is just one instance of it in the component? Being able to splat assigns here is much better IMO, not just because itā€™s more convenient but it removes duplication that provides no extra value (unless you arenā€™t using declarative assigns for some reason). In any event, this should be documented as to where this works and why. Otherwise using it in a shared codebase sorta sets a poor precedent that it is generally ok.

Thanks for pointing this out!

3 Likes

Iā€™ve wrapped live components in functional components to gain props for a bit, but ultimately stopped since I thought the functional fake mask created a dangerous false identity for what is going on and could confuse people who donā€™t have intimate knowledge of the system.

I would love it if we could get some kind of prop help from the framework for live component. The current situation does not seem ideal.

This is exactly why I always prefix them with live_.

1 Like