Problems with nested components

Hi!

I have a strange problem when creating a “Tree” component where I have to recursively call a function to create each branch of the tree.
My problem is that the output looks flattened and I cannot see why.

Component code:

defmodule HubWeb.Components.Tree do
  @moduledoc false

  alias Phoenix.LiveView.JS

  use Phoenix.Component
  attr :id, :string, required: true
  attr :branches, :list, default: []
  attr :class, :string, default: ""
  attr :branch_click, :any, default: nil

  slot :branch, required: true do
  end

  def tree(assigns) do
    ~H"""
    <ul id={@id} class={["selection-none", @class]}>
      <.branch id={@id} branches={@branches} branch={@branch} branch_click={@branch_click} />
    </ul>
    """
  end

  defp branch(assigns) do
    ~H"""
    <li :for={item <- @branches} id={item.id} class="p-1">
      <%= render_slot(@branch, item) %>

      <div id={"#{@id}_#{item.id}_children"} class="hidden pl-4">
        <.branch id={@id} branches={item.children} branch={@branch} branch_click={@branch_click} />
      </div>
    </li>
    """
  end
end

To call it you would use:

<.tree id="tree" class="mr-20" branches={[
        %{
          id: "id1",
          name: "Vaccine",
          children: [%{id: "id2", name: "Vaccines"}, %{id: "id3", name: "Accessories"}]
        }
      ]}>
      <:branch :let={leaf}><%= leaf.name %></:branch>
    </.tree>

Expected result:

<ul id="tree" class="selection-none mr-20">
  <li id="9fd02391-38a1-484b-84e7-c7c400315875" class="p-1">
    Vaccine
    <div id="tree_9fd02391-38a1-484b-84e7-c7c400315875_children" class="hidden pl-4">
      <li id="7dddbbf2-28f8-4639-8a37-7386b16705cf" class="p-1">
          Vaccines
          <div id="tree_7dddbbf2-28f8-4639-8a37-7386b16705cf_children" class="hidden pl-4">
          </div>
        </li><li id="0d6884e4-d9e4-4485-a5eb-b1a36fb39950" class="p-1">
          Accessories     
          <div id="tree_0d6884e4-d9e4-4485-a5eb-b1a36fb39950_children" class="hidden pl-4">
          </div>
        </li>
      </div></li>
</ul>

Actual result:

<ul id="tree" class="selection-none mr-20">
  <li id="9fd02391-38a1-484b-84e7-c7c400315875" class="p-1">
    Vaccine
    <div id="tree_9fd02391-38a1-484b-84e7-c7c400315875_children" class="hidden pl-4">
      </div></li><li id="7dddbbf2-28f8-4639-8a37-7386b16705cf" class="p-1">
    Vaccines
    <div id="tree_7dddbbf2-28f8-4639-8a37-7386b16705cf_children" class="hidden pl-4">
    </div>
  </li><li id="0d6884e4-d9e4-4485-a5eb-b1a36fb39950" class="p-1">
    Accessories
    <div id="tree_0d6884e4-d9e4-4485-a5eb-b1a36fb39950_children" class="hidden pl-4">
    </div>
  </li>
    </ul>

You can see that the sub-branches are not nested inside the div in the parent, but instead they are just appended together with the parent…

Anyone who can see what I might be doing wrong? Or is this not possible?

Thank you!

I’d start by making sure you’re producing valid html: <li> is not a valid child of <div>. Invalid html might cause your browser to close tags, where you didn’t explicitly put a closing tag.

1 Like

Darn it!.. You were totally correct!

I should have used ul where I used the div and all was good right away!

<div id={"#{@id}_#{item.id}_children"} class="hidden pl-4">
    <.branch id={@id} branches={item.children} branch={@branch} branch_click={@branch_click} />
  </div>

into

<ul id={"#{@id}_#{item.id}_children"} class="hidden pl-4">
    <.branch id={@id} branches={item.children} branch={@branch} branch_click={@branch_click} />
  </ul>