Do attrs not cause compiler warnings for Phoenix.LiveComponent?

I added several required attr macros to my LiveComponent that was generated with phx.gen.live. I tried removing these attributes from where I’m using that component, but I don’t see any compiler warnings in my console. Do these only alert for Phoenix.Component, and not LiveComponent? I see from the source that LiveComponent uses Component so I’d imagine they’d behave the same way.

Versions:

{:phoenix, "~> 1.7.0-rc.0", override: true},
{:phoenix_live_view, "~> 0.18.3"},

Component:

attr :title, :string, required: true, doc: "Page title"
attr :navigate, :string, required: true, doc: "Route to navigate to after successful save"

@impl true
def render(assigns) do

Function Use:

<.live_component
  module={IdoWeb.TripLive.FormComponent}
  id={@trip.id}
/>

attr and slot are tools for function components, not live components. You can make your live component use one top level function component however.

2 Likes

Ah ok, that’s disappointing. I figured the attrs is just as useful for LiveComponent as Component. Should Phoenix prevent using the attrs macro in LiveComponent if it’s not going to provide anything?

Thanks!

Technically render/1 is a function component, but the whole LiveComponent isn’t. You’re not doing <SomeMod.render />, because that wouldn’t make it stateful. Also keep in mind that the assigns passed to the LiveComponent (either passed in or using send_update) don’t directly map to assigns used with render/1, so I’m not sure how this would be supposed to be validated in the first place.

Got it. Then is there a recommended way to validate inputs to a live component? The hard thing for me is refactors- I add an attribute to my LiveComponent, but forget to update an instance that uses it. Hopefully a test will catch my mistake.

Just pattern match the render function to make it fail sooner?

def render(%{title: _title, navigate: _navigate} = assigns) do

What about providing a wrapper function component that creates the live component?

@doc “Renders the thing as a LiveComponent”
attr …
slot …

def live(assigns) do
  ~H”””
    <.live_component module={__MODULE__} … />
  “””
end

def render(assigns) do
  …
end

# use as
<.MyComponent.live whatever={…} />

No idea if this is a good pattern to use, but I think it gets what you want.

5 Likes

Oh interesting. I wonder if passing through the assigns breaks any sort of caching? Guessing this will be no different from how it typically behaves?

<.live_component module={__MODULE__} {@assigns}/>

Would the wrapper component absort the id attr, and then the inner component would be trying to use the same id (or no id)?

I am reading Elixir in Action and came across a feature called Registered Module Attributes, like @doc & @moduledoc.

So I was thinking, perhaps later LiveView can have 2 more registered module attributes, @attr & @slot, which will give meta information about the module itself.

So both, function components and live components can have feature parity.

There’s no fundamental difference here vs. a local function component that renders the live component! The wrapper would just declare id as a required attribute and pass it along to the live component. Function components don’t “eat” id — there’s nothing special about id (or any attribute that doesn’t start with :, like :if, :for etc).

Not sure exactly what you mean by caching — as in, the LiveView diffing algorithm? Shouldn’t run into any issues here!

I was referring to the requirement for LiveComponent’s to have an id. I thought that the id might be used by Phoenix so it wouldn’t be a part of the assigns. I don’t think there’s any such thing for function components, so the id will just be passed through no problem like you suggest.