Live_patch broken with custom LiveComponent update/2 implementation. How to implement update/2?

I have a LiveComponent used for master/detail handling of data.The component is used once in its parent LiveView.

Navigating from master to detail is handled with live_patch links and pattern matched handle_params function that update the required LiveView assigns that are then updated in the LiveComponent.

So far, no problem. In my first implementation, I passed the required data for the LiveComponent from the parent LiveView to the LiveComponent with live_component/4.

My problem is now that I want to initialize the data from the LiveComponent and not the LiveView. In my case, update/2 looks like the right place to do it. So I do this:

  def update(assigns, socket) do
    assigns = Map.merge(socket.assigns, assigns)
    items = Items.get_items_by(assigns.current_user)
    socket =
      %{socket | assigns: assigns}
      |> assign(:items, items)
    {:ok, socket}
  end

The problem is that as soon as I add this function, live_patch does not work anymore. When I click the link in the master view, the URI still gets updated but the detail does not show and vice versa. If I type the right url in the browser to navigate to it, the correct master / detail data is displayed.

This leads me to think that the overridden update/2 function does more than merging the assigns, as the function documentation suggests.

What is the correct way to implement update/2 function? Is there a way to call the overridden function to ensure the default behaviour remains, and then update the assigns with my own values?

As a workaround, initializing data in preload/1 works:

  def preload(list_of_assigns) do
    Enum.map(list_of_assigns, fn assigns ->
      items = Items.get_items_by(assigns.current_user)
      Map.put(assigns, :items, items)
    end)
  end

But what if I need to initialize data in update/2instead of preload/1?

Thanks.

1 Like

I found the answer in the LiveView generated demo project, and its super simple, only this is required:

def update(assigns, socket) do
    {:ok,
      socket
      |> assign(assigns)}
end

The assigns are merged, as the docs states. However assign/2 is used for this, merging manually does not work.
I spent almost a week on side effects of this problem (mostly communication problems between LiveComponents: that’s not easy without send_update…) just to find out the solution is super easy. So I leave this for reference.

2 Likes