Handing events between parent LiveView and child LiveComponent(s)

Hi folks,

I just started playing with LiveView and not sure how to proceed with the following issue:

I have a parent LiveView module. That module renders a form template. That template has a simple button with emitted event, like so:

<%= form_for :blank, "#", [phx_submit: :add_new_company] %>
  <%= submit "Add" %>
</form>

and, in the same template, I render a dynamic list of LiveComponents:

<%= for company <- @companies do %>
  <%= live_component @socket, MyApp.CompanyComponent, id: @id, company: company %>
<% end %>

where @id is the id of a parent LiveView being assigned on mount/2 callback.

That event is handled by a corresponding handle_event/3. Inside that handler I add a new company to a list of already existing companies. That part works and on each new button click the new company form is being rendered correctly. However, when I try to send an event from LiveComponent form, I’m getting no function clause matching in MyApp.CompanyView, which is the parent LiveView and not handle_event/3 defined on the component. After reading this portion of LiveComponent docs I feel like this is exactly what I’m already doing, however the event emitted my component is being routed to the parent LiveView and not to the underlying child component. Am I missing something?

I think you need to set a phx-target and DOM id per the section just above: Phoenix.LiveComponent — Phoenix LiveView v0.20.2

With form_for, you can set a DOM id by passing as: "company-#{@id}". Pass the same id to phx_target and it should call the handle_event of the component when you submit.

I’m surprised reusing a single ID for all CompanyComponent instances worked at all… I think normally you want that to be unique per entry (using e.g. id: company.id instead). The doc says:

Stateful components are identified by the component module and their ID. Therefore, two different component modules with the same ID are different components.

Thanks for the response!

For the sake of this example, I set ID to a static value (let’s say 10). With the following code in my LiveComponent template:

<%= f = form_for @company, "#", [as: "company-#{@id}", phx_target: "company-#{@id}", phx_submit: :save] %>
  <%= label f, :name %>
  <%= text_input f, :name %>
   
  <%= submit "Save" %>
</form>

Nothing happens upon submission. I feel like I’m missing the idea behind phx_target and what it supposed to target - my understanding is that when I add the as: "company-#{@id}" this adds the id to each tag inside my form and then phx_target is supposed to capture all those tag values upon submission and send that as an event to the LiveComponent. However, not sure what I’m doing wrong with my code.

Hey @kminevskiy your phx_target value needs to be an actual query selector eg: phx_garget: "#company-#{@id}" assuming your component DOM element has the id company-10.

2 Likes

Thank you very much, @benwilson512 and @dom. Missed that query selector. It works now.

1 Like