Why is live_component/3 called twice in this example and what's the role?

I’ve been reading the book “Programming Phoenix LiveView” for a while now. Basically on our index page we’ve got products listed and then when we click on the edit button a modal is rendered with a form with the product details which you can edit.

Here’s a screenshot:

After taking a look at the code, I can see the snippet below in index.html.leex which does the rendering of the modal:

<%= if @live_action in [:new, :edit] do %>
  <%= live_modal @socket, PentoWeb.ProductLive.FormComponent,
    id: @product.id || :new,
    title: @page_title,
    action: @live_action,
    product: @product,
    return_to: Routes.product_index_path(@socket, :index) %>
<% end %>

And if I navigate into where live_modal/3 is defined, I can see:

  def live_modal(socket, component, opts) do
    path = Keyword.fetch!(opts, :return_to)
    modal_opts = [id: :modal, return_to: path, component: component, opts: opts]
    live_component(socket, PentoWeb.ModalComponent, modal_opts)

Which then calls live_component/3. However if we look into PentoWeb.ModalComponent we see that in the render function live_component/3 is called as well.

# pento_web/live/modal_component.ex 

defmodule PentoWeb.ModalComponent do
  use PentoWeb, :live_component

  @impl true
  def render(assigns) do
    <div id="<%= @id %>" class="phx-modal"
      phx-target="#<%= @id %>"

      <div class="phx-modal-content">
        <%= live_patch raw("&times;"), to: @return_to, class: "phx-modal-close" %>
        <%= live_component @socket, @component, @opts %>

  @impl true
  def handle_event("close", _, socket) do
    {:noreply, push_patch(socket, to: socket.assigns.return_to)}

And there’s this diagram from the book:

So could you please tell me what’s role of live_component here and why it’s called twice? This might be an unimportant question but I am just wondering the details.

Hmm, I think now I’ve got it. The first one is to render the ModalComponent and then the second one is to render the FormComponent inside that ModalComponent. :duck: :duck:

But correct me if I am wrong, please. :smiley:


The description was literally under the diagram. :man_facepalming:

The ProductLive.Index template calls a helper function, live_component/3, that in turn calls on the modal component. The modal component renders a template that presents a pop-up to the user. This template renders yet another component, the form component, via a call to live_component/3.

1 Like