Emit LiveComponent event to both the LiveComponent as well as its parent LiveView

Hello fellow Elixters :wave:

I am quite new to Phoenix LiveView and just started testing Surface UI. So far, it all feels very natural and well-designed. So kudos to everyone involved in building it! :slight_smile:

Now I got stuck with something I expect to be a fairly easy and common problem.

Situation

I have a LiveView with a nested LiveComponent. For the sake if this example, let’s say the LiveComponent renders a card which should be highlighted when selected (clicked). The LiveComponent should therefore emit an event when clicked. This event should be handled both in the LiveComponent to toggle the highlighting as well as optionally within the LiveView to, say, track which cards are selected.

First of all: Does it at least make sense what I am trying to do? :slight_smile:

Second: Currently I see two possible ways to implement it:

  1. Emit the event with the LiveComponent as its target (i.e., phx-target={@myself} in plain LiveView, or on-click="my-click-event" in Surface UI). Then inside handle_event/3 toggle the card highlight, forward the event with send(self(), ...) to the LiveView and, in there, handle it in handle_info/2. This works, but has the drawback that the LiveComponent’s parent must handle the message with handle_info/2 unless it wants its mailbox to fill up. Moreover, it would feel so much more natural if I handle the message from LiveComponent in the LiveView’s handle_event/3 as well.
  2. Emit the event and make the LiveComponent’s parent the target – that is, the LiveView (in Surface UI via prop my_click, :event in the LiveComponent and on-click={@my_click} inside render/1). There, catch the event in handle_event/3 and use send_update/3 to cause the LiveComponent to toggle the card’s highlight. Has the (IMHO) severe architectural drawback that the parent (here: LiveView) has to do the wiring so that the child (here: LiveComponent) renders as expected. Definitely don’t want to go down this road.

Both variants don’t feel too good to me. Essentially, what I would like to do is to somehow configure the phx-click (or :on-click in Surface UI) to target both the LiveComponent as well as the LiveView. Is that somehow possible?

Or: What’s the LiveView-idiomatic way to implement my scenario?

Best
Carsten

How do you avoid handling this message and filling up the mailbox?

I wouldn’t get caught up on handle_event vs handle_info. You’re likely to end up with plenty of handle_info in the long run. All your PubSub messages, messages to self, messages from other processes will land there.

Another option is to use function components or stick with livecomponents and have highlighted as an assign, handling state in the liveview.

If you go the send_update route, you can define a function on the component that wraps send_update, e.g. Card.highlight(id).

1 Like

I don’t think this is true. Messages that go to your LiveView are handled here. If your view defines a handle_info callback, and it doesn’t handle the message that was just received, your view will simply crash. If your view doesn’t define a handle_info, a warning is emitted (but the message was still handled). Either way the mailbox won’t fill up.

I’m assuming that you need a LiveComponent to isolate state handling and logic and that function components are not a desirable option. I say this because in your specific example you could get away with a function component and put all state handling in the LiveView, but I presume your example is an oversimplification of something else.

I generally use a combination of (1) and (2): events from the LiveComponent are forwarded to the LiveView by sending a message, and if the LiveView has to communicate something to the LiveComponent, it does so using send_update/3 (it’s all message passing anyway under the hood).

I totally agree. I think this is a drawback of the current architecture. LiveComponents should be able to emit events for their parents IMO. I believe Chris McCord mentioned a rethinking of the API to communicate between LiveViews and LiveComponents as ongoing work in his last ElixirConf talk, so maybe something will happen there soon :crossed_fingers:

2 Likes

Many thanks for your replies, @cmo and @trisolaran. :slight_smile: Really appreciate it!

As always in life: taking a look at the source of truth helps to build the correct mental model. :smiley: Thank you for pointing me to the sources. My concerns about an overflowing mailbox thus were indeed wrong.

As you also acknowledged: it is a bit unfortunate that this seems to be the current best solution (although I only have architectural concerns with (1) rather than (2)). But hey: then I will go with (1) for now, accept that the LiveView now should handle the message (and warnings are printed if it doesn’t) and also have my fingers crossed for future API improvements. :slight_smile: