VirtualizedTreeLayout Expand/Collapse and Search

After I change to VirtualizedTreeLayout with TreeLayout (because I wont lost the Fancy things that was provided by the TreeLayout) the functionality of Collapse/Expand and Search are not working quite well.
I think that because the component relies on rendered model to do search and on collapse/expand we are having this problems.
There are any straightfoward solution for this or I will need to implement how to stay collapsed when the items return to viewPort and how to find collection instead of rendered items?

Yes, that’s true – you won’t be able to use the standard “TreeExpanderButton”.

Instead you’ll need to implement your own equivalent button that explicitly sets an “invisible” property on the node data for all of the subtree data. Then you’ll need to augment the onViewportChanged function to also check that the node data is not invisible before deciding to add the node data to the Diagram’s model.

You will also notice that when the user collapses or expands a node, there’s no automatic layout. That is because setting isLayoutPositioned: false in the Node template prevents all of the nodes from participating in any layout. So as part of the implementation of your replacement collapse/expand button you will also need to explicitly invalidate the Diagram.layout.

In fact I’m trying to use the standard “TreeExpanderButton” but with customized listeners for “TreeExpanded” and “TreeCollapsed”.
On “onViewportChanged” inside this statement “if (!model.containsNodeData(child))” I take a look if this brand new item was collapsed and set the property: “isTreeExpanded” to false.
On “removeOffscreen” I need to left the diagram create the items that is inside the collapsed fathers (before this the image for expanded and collapsed disappears when removeOffScreen runs).
But I’m having one problem with this approach. When I go off screen and move back the sons are not appearing and the image is ok (a plus sign) but the grandsons become visible, like on the image:

Maybe invalidating the diagram will make the space fades away, will be good, I don’t know yet how to do it, but I’ll take a look.
But I wanna know if I’ll need to make all childrens of a collapsed father children fade away manually too.

I have tried to follow your suggestion about implementing the
treeExpanderButton for a Virtualized Layout. Because seems logical that
if there are no “viewport” available to move the Diagram, there will be
no problems onViewPort Changing (and render again, problems that I have
with my approach).
Then I copy the code for the treeExpanderButton available on your site:

            function makeVirtualizedTreeExpanderButton() {
                var $ = go.GraphObject.make;
                var button =
                  $("Button",  // NOTE: this creates a "Button" and extends it
                    $(go.Shape,  // the icon
                      {
                          name: "ButtonIcon",
                          figure: "MinusLine",  // default value for isTreeExpanded is true
                          desiredSize: new go.Size(6, 6)
                      },
                      // bind the Shape.figure to the Node.isTreeExpanded value using this converter:
                      new go.Binding("figure", "isTreeExpanded",
                                  function (exp, node) {
                                      var fig = null;
                                      var button = node.panel;
                                      if (button) fig = exp ? button["_treeExpandedFigure"] : button["_treeCollapsedFigure"];
                                      if (!fig) fig = exp ? "MinusLine" : "PlusLine";
                                      return fig;
                                  })
                          .ofObject()),
                    // assume initially not visible because there are no links coming out
                    { visible: false },
                    // bind the button visibility to whether it's not a leaf node
                    new go.Binding("visible", "isTreeLeaf",
                                   function (leaf) { return !leaf; })
                        .ofObject()
                  );

                // tree expand/collapse behavior
                button.click = function (e, obj) {
                    var node = obj.part;  // OBJ is this button
                    if (node instanceof go.Adornment) node = node.adornedPart;
                    if (!(node instanceof go.Node)) return;
                    var diagram = node.diagram;
                    if (diagram === null) return;
                    var cmd = diagram.commandHandler;
                    if (node.isTreeExpanded) {
                        if (!cmd.canCollapseTree(node)) return;
                    } else {
                        if (!cmd.canExpandTree(node)) return;
                    }
                    e.handled = true;
                    if (node.isTreeExpanded) {
                        cmd.collapseTree(node);
                    } else {
                        cmd.expandTree(node);
                    }
                    diagram.layoutDiagram(true);
                };
                return button;
            }

and added that last line on button.click = function (e, obj) { … }
I tought this command will invalidate all the layouts inside the diagram and force the renderization. Before this I have tried:

myDiagram.layout.invalidateLayout();

and

myDiagram.layout.isValidLayout = false;

but after this I saw that if isOngoing is false (as in the case of virtualizedTreeLayout) theese options will not work properly.
Because diagram.layoutDiagram(true) is not working as I expected and when I change the viewPort the nodes becomes available even if it is childer of a colapsed parent.
Do you have any sugestions on this case?

Did you do what I suggested involving an “invisible” property on the node data?

Ok Walter, you’re right, I miss something.
On my expander click I’ll put this “visible” to false/true on all child elements. (not done)
On my expander creation I’ll need to check if he has children and allow the button to be created (because when there are no child nodes on the model collection the expander button is not renderend) (not done)
On my onViewPortChanged I’ll check if it is visible and add or not. (not done)
Call onViewPortChanged manually. (not done)
But I do not understand why I need to invalidate the diagram?
And if I’m doing it in the correct manner using the code above?

This is the “steps” that I need to accomplish?

Are you asking about invalidating the Diagram.layout? The normal layout invalidation cannot occur because nodes and links either do not exist or are being added or removed all the time.

I’ve made all this steps that I said before and the button still don’t working properly. I mean. When I’m on screen and I ask for Collapse/Expand this works well. But when I re-create the button inside onViewportChanged something goes wrong and the code stop works, here the code for onViewportChanged:

    function onViewportChanged(e) {
        // make sure there are Nodes for each node data that is in the viewport
        // or that is connected to such a Node
        var viewb = myDiagram.viewportBounds;  // the new viewportBounds
        var model = myDiagram.model;

        var tdata = myTreeModel.nodeDataArray;
        for (var i = 0; i < tdata.length; i++) {
            var d = tdata<em>;
            if (!d.bounds) continue;
            if (d.bounds.intersectsRect(viewb)) {
                if (d.visible) {
                    if (!model.containsNodeData(d)) {
                        model.addNodeData(d);

                        if (colapsedNodes.indexOf(d.key) > -1) {
                            var resultNode = myDiagram.findNodeForKey(d.key);
                            var diagram = resultNode.diagram;
                            var cmd = diagram.commandHandler;

                            if (cmd.canCollapseTree(resultNode)) {
                                cmd.collapseTree(resultNode);
                                diagram.layoutDiagram(true);

                                updateChildVisibility(resultNode.Kl, false, true);
                            }
                        }
                    }
                    // make sure links to all parent nodes appear
                    var parent = d._parent;
                    while (parent && !model.containsNodeData(parent)) {
                        model.addNodeData(parent);
                        parent = parent._parent;
                    }
                }
            }
        }

and this is the part that I added:

                        if (colapsedNodes.indexOf(d.key) > -1) {
                            var resultNode = myDiagram.findNodeForKey(d.key);
                            var diagram = resultNode.diagram;
                            var cmd = diagram.commandHandler;

                            if (cmd.canCollapseTree(resultNode)) {
                                cmd.collapseTree(resultNode);
                                diagram.layoutDiagram(true);

                                updateChildVisibility(resultNode.Kl, false, true);
                            }
                        }

But even when the key is found the {cmd.canCollapseTree(resultNode)} results false and I’m not able to send the collapseTree command. I think this has something to do with the lifecicle, because I just added this node in diagram. But I’m not sure. Any tips?

The user’s scrolling or zooming the diagram should not cause nodes to be collapsed or expanded.

Normally the Node.isTreeExpanded state is kept on the Node. With virtualization you want to put that state into your model data. That means you need to replace the normal Node collapseTree and expandTree functionality with your own functions that work on the node data – that “invisible” property I suggested that you add to the node data.

And then the “ViewportBoundsChanged” DiagramEvent listener can just look at that “invisible” property to decide whether to bother adding a real Node to the Diagram.

That also means that the tree layout also should respect this “invisible” property on the node data, so that it can ignore that node and all of its children.