sb logoToday I Learned

Working with nested associations in LiveView

Creating forms of nested associations in LiveView can be intimidating and a head-scratcher at first. It turns out it is actually very easy!

In my sports example, I have a Match which has many PlayersInMatch, which has many PlayersInQuarter. We start with a changeset around match Ecto.Changeset.change(match)with everything preloaded.

The trick is, in order to update everything in the handle_event callback, you need to make sure that the id of all the associations is present in the form, and you do this by rendering hidden_inputs_for(assoc) at each level.

<%= for player <- @players do %>
    <%= hidden_inputs_for(player) %>
    <li class=""><%= player_name(player) %></li>
        <%= for piq <- inputs_for(player, :players_in_quarter) do %>
            <%= hidden_inputs_for(piq) %>
            <li class="border pl-2 py-1"><%= number_input piq, :points %></li>
        <% end %>
    </li>
<% end %>

When the callback is called, you will get all of the nested associations in the Match. Then it is simple as

 def handle_event("validate", %{"match" => params}, socket) do
   changeset = 
    socket.assigns.match
    |> Ecto.Changeset.cast(params, [])
    |> Ecto.Changeset.cast_assoc(:players_in_match, with: &PlayerInMatch.update_changeset/2)
  {:noreply, assign(socket, :changeset, changeset)}
end