List of nested maps not working from liveview

Hello, I have a small phoenix app with just one ecto model called Office with the following schema for parent relationship

defmodule Tree.Arma.Office do
  use Ecto.Schema
  import Ecto.Changeset

  schema "offices" do
    field :name, :string
    belongs_to :parent, Tree.Arma.Office
    has_many :children, Tree.Arma.Office
    timestamps(type: :utc_datetime)
  end

  @doc false
  def changeset(office, attrs) do
    office
    |> cast(attrs, [:id, :name, :parent_id])
    |> validate_required([:name])
  end
end

Inside Arma context I have the following helper function to retrieve a linear list of offices in parent-children relations and works perfectly

def list_descendants(id) do
    base_query =
      Office
      |> where(id: ^id)

    recursive_query =
      Office
      |> join(:inner, [next_descendant], parent in "descendants_list",
        on: next_descendant.parent_id == parent.id
      )

    descendants_query = base_query |> union_all(^recursive_query)

    {"descendants_list", Office}
    |> recursive_ctes(true)
    |> with_cte("descendants_list", as: ^descendants_query)
    |> where([o], o.id != ^id)
    |> Repo.all()
  end

I have also defined the following functions to retrieve a nested map of descendants:

  def nested_tree(parent_id) do
    flat_list = list_descendants(parent_id)
    nester(flat_list, parent_id)
  end

  def nester(flat_list, root_id) do
    lookup = Enum.group_by(flat_list, & &1.parent_id)
    with_children(root_id, lookup)
  end

  defp with_children(root_id, lookup) do
    lookup
    |> Map.get(root_id, [])
    |> Enum.map(&nest_one(&1, lookup))

  end

  defp nest_one(row, lookup) do
    %{row | children: with_children(row.id, lookup)}
  end

So, if we have an Office with id=4 and call nested_tree(4) the function returns a nested map of all the descendants from the Office with id=4.

The problem is that when i call nested_tree from inside an iex -S mix shell all works fine; but when i the function is called from the liveview that shows the details for a sibgle Office, nested_tree() return an empty list. This is the code of the liveview:

defmodule TreeWeb.OfficeLive.Show do
  use TreeWeb, :live_view

  alias Tree.Arma

  @impl true
  def mount(_params, _session, socket) do
    {:ok, socket}
  end

  @impl true
  def handle_params(%{"id" => id}, _, socket) do
    {:noreply,
     socket
     |> assign(:page_title, page_title(socket.assigns.live_action))
     |> assign(:office, Arma.get_office!(id))
     |> assign(:office_tree, Arma.nested_tree(id))
    }
  end

  defp page_title(:show), do: "Show Office"
  defp page_title(:edit), do: "Edit Office"
end

I’ve found that if in the liveview the id of the office is hardcoded when calling nested_tree() it works!
So for example if in the liveview I put:

|> assign(:office_tree, Arma.nested_tree(4)

it works!

I’m really stuck with this, so if anybody can help me I really appreciate!!

What happens if you call your function with a quoted value, ”4”?

Hello and many thanks for your reply!

if I call nested_tree(“4”) returns an empty list

Then maybe passing the ID either as a string or as an integer is the reason for the different behaviour you observe.

The params map in the handle_params function always contains string values as far as I know. What happens if you parse to an integer before passing it to your functions?

@impl true
  def handle_params(%{"id" => unsigned_id}, _, socket) do
    id = String.to_integer(unsigned_id)

    {:noreply,
     socket
     |> assign(:page_title, page_title(socket.assigns.live_action))
     |> assign(:office, Arma.get_office!(id))
     |> assign(:office_tree, Arma.nested_tree(id))
    }
  end

It works! perfect explanation and solution to the problem!
Many thanks for your fast reply!