I'm using Vega JS for building a tree chart. In general, my question is the following:
Vega documentation has a great example of tree layout. How can I extend it with an ability to collapse & expand its nodes?
To be more specific, let's consider an example of tree chart that I'm building here in Vega Editor.
If you click on the nodes, they will toggle (expand or collapse), allowing you to see particular branches of the tree. This feature works fine unless you try to collapse the top-level node (region) while keeping the second-level nodes (districts) expanded. In that case the tree will look like this:
It happens because of the way I handle this interaction:
- When you click on a node,
toggledNodesignal is triggered, which in turn triggerstoggleaction in theexpandedNodesdata array. I.e. by clicking on a node, I add or remove that node to theexpandedNodesarray (more precisely, we add/remove a reduced object with onlynameproperty) - Thus
expandedNodesdata contains information about which nodes are explicitly expanded. But it doesn't know if those expanded nodes are inside of a collapsed parent node. - Then, to find out which nodes are actually visible, I use
visibleNodesdata. There I applyfiltertransform with the following expression:!datum.parent || indata('expandedNodes', 'name', datum.parent). I.e. I check only one level up: if the node's parent is present in theexpandedNodesarray , I consider the node as visible.
The problem is the following: I can't find any way of extending this functionality across multiple levels.
Probably I could write some hooks to check the same condition across 2 or 3 levels, e.g:
!datum.parent ||
indata('expandedNodes', 'name', datum.parent) &&
indata('expandedNodes', 'name', datum.myCustomFieldWithParentNode.parent) &&
indata('expandedNodes', 'name', datum.myCustomFieldWithParentNode.myCustomFieldWithParentNode.parent)
But it seems too complex for such a simple problem, and also it's not a final solution. In theory, a tree may contain dozens of nesting levels: what to do then?
I found one useful expression in Vega: treeAncestors. I could easily write a solution in JavaScript, where I have loops and array methods such as .some() and .every(). But apparently Vega doesn't support any expressions to iterate over an array. So even though I can get an array of tree node ancestors with treeAncestors function, I can't do anything with it to verify that all ancestors are expanded.
Either my approach is wrong, and somebody can find a better algorithm for doing the same, which doesn't require iterating over arrays (except for data and indata expressions) - or it's a current limitation of Vega.
