Expandable table rows with live view

:wave:

How would one implement expandable table rows with live view? I’ve tried nested components approach:

# root live view
~L"""
<table>
<%= for child <- @children do %>
  <%= live_component @socket, ExpandableRow, id: child.id, item: child %>
<% end %>
</table>
"""

# expandable row component
~L"""
<tr phx-click="toggle-expand" phx-target="<%= @myself %>">
  <td><%= @item.name %></td>
</tr>
<%= for child <- @children do %>
<%= live_component @socket, __MODULE__,
  id: child.id,
  item: child %>
<% end %>
"""


def handle_event("toggle-expand", _params, %{assigns: %{children: []}} = socket) do
  {:noreply, assign(socket, children: [%{id: "asdf", name: "asdf"}])}
end
def handle_event("toggle-expand", _params, socket) do
  {:noreply, assign(socket, children: [])}
end

and the update (new children) is being sent to the browser but not applied.

1 Like

Live view probably can’t handle for block at the component root level, so instead I’m adding and removing trs manually:

  def handle_event("toggle-expand", %{"item-id" => item_id}, socket) do
    items = toggle_expand(socket.assigns.items, item_id)
    {:noreply, assign(socket, items: items)}
  end

  # we have found the item
  defp toggle_expand(
         [%{id: id} = item | [%{path: path} | more_possible_children] = not_children],
         id
       ) do
    if id in path do
      # and it has its children right after -> we remove them
      [item | splice_children(more_possible_children, id)]
    else
      # and there are no children -> we fetch them
      [item | Items.fetch_item_children(item)] ++ not_children
    end
  end

  # we keep any other item as is
  defp toggle_expand([other_item | rest], id) do
    [other_item | toggle_expand(rest, id)]
  end

  defp splice_children([%{path: path} | rest] = no_more_children, id) do
    if id in path do
      splice_children(rest, id)
    else
      no_more_children
    end
  end
1 Like

I am also building a real world project with live view and I gave up very soon of using live view for every single interaction the user has with the page, unless some busines logic needs to be performed in the backend.

Why?

Try to use your project in a commute trip in a train, bus or whatever and you quickly realize how sluggish the user experience will be… You need to remember that the internet on this situations is far from the ideal scenario you have in localhost or even when in a fixed location with a good signal and connecting to a backend in the other side of the world.

So my advise is to only use Live View when data needs to have business logic applied and/or fetched, but not use it to try to enrich the user interactivity with the site, because you will be doing exactly the opposite. Instead I am just using plain javascript and the bindings provided by live view to trigger them.

2 Likes