Using Liveview to implement a tree explorer, will expanding nested nodes work with temporary assigns?

I’m thinking of using Phoenix Liveview to implement a tree explorer where one can drill down on parts of the tree which is collapsed by default. You can click on a node to expand it, revealing its children if it has any: (think of the file directory browser in the left pane of vscode).

The tree might be quite large (many elements and arbitrary depth) so I don’t want to have all of it sitting in the genserver memory so I was thinking of using temporary assigns so the data is passed and rendered on the client and then cleared from the genserver. In fact at first I want to pull in the first level (root’s) children only to begin with. And when one of those children is expanded by clicking on it, only then do I query it’s children and add them to the DOM, and so forth, lazily loading small portions at a time when each node is clicked and expanded.

My schema can be a simple adjacency list, something like:

Table entities:

  • id
  • parent_id
  • children_count
  • name
    has_many :children, Entity, foreign_key: :parent_id

The view would need to be able to handle nesting of entities (nested lists) of any depth so I was thinking of writing some recursive function using content_tag, that would recurse into a nested :ul if a node is preloaded with children (the idea being, we only populate the children attribute on the node if it was expanded).

For each entity that can be expanded, render a [+] icon with a phx-click that sends the id of the entity we want to expand. Then in the liveview handler, query the children of that id, and with that data… eh… do what?

Here’s where I get stuck. How do I update the socket assigns and put the expanded children into the tree at the right place, if using temporary assigns? I have no data retained in the genserver in order to find the nested sub node to update it’s children attribute.

Or…

can I just do socket assign and send down the expanded entity with children and hope that liveview can update the correct element simply because the replacement data shares the same DOM ID, no matter how nested down in the tree that element might be? All the examples I’ve read regarding use of temporary assigns operate on a flat list, so I don’t know if this will work on deeply nested DOM elements.

Hopefully I’ve explained what I have doubts about in a clear enough way…

I wanted to vet this idea and get some clarity, opinions or advice on this before I get too far. thanks!

1 Like

Is your tree going to have more than a couple of thousand nodes? If true, I don’t think a simple expandable tree is the right way to explore the data. You most likely need to have other ways to prune the tree down to a reasonable level before presentation.

Now if your tree is pruned down to a few thousand nodes or less; I think it should be reasonable to store the whole tree structure in the assigns. Additional data such as large text can stay in database and be loaded as needed and put in temporary assigns.

1 Like

probably not more than a few thousand. I’m trying to be conservative with memory usage on the server though.

I’m curious to know whether the DOM element replacement will work if the element with matching id can be nested anywhere in the container or only at the top level.

it seems like what you want is not liveview, but a regular controller that send a fragment of html on demand and a client side part that can inject this fragment in arbitrary place in the dom, based on an given dom id.

if i were you i will just do it myself, instead of trying to bend liveview backwards.

Did you manage to make it work? I am starting a project that will show files in a folder and what you described would match my requirements.

With the introduction of Streams in LiveView, the concern about server side memory usage is much less of an issue. LiveView Streams are nestable and you can find an example in the todotrek repo – specifically to stream individual todos within todo lists that are also streamed.

This means you could stream your file tree with all nodes rendered to be collapsed by default and have expanding/collapsing behavior be a completely client side concern. If you need to track node expand/collapse state for “restoring”, you could track that separately in the LiveView process with an assign and/or in your preferred cache/database.

1 Like