Expanding a Leaf node of Incremental Tree overlaps new nodes on the existing nodes

I created an Incremental Tree as:

      `var $ = go.GraphObject.make;  // for conciseness in defining templates            

        myDiagram =
          $(go.Diagram, "myTreeDiv",  // must name or refer to the DIV HTML element
            {
                initialContentAlignment: go.Spot.Center,
                initialAutoScale: go.Diagram.UniformToFill,
                contentAlignment: go.Spot.Center,
                layout: $(go.ForceDirectedLayout),
                // moving and copying nodes also moves and copies their subtrees
                "commandHandler.copiesTree": true,  // for the copy command
                "commandHandler.deletesTree": false, // for the delete command
                "draggingTool.dragsTree": true,  // dragging for both move and copy
                "undoManager.isEnabled": true
            });

        // Define the Node template.
        // This uses a Spot Panel to position a button relative
        // to the ellipse surrounding the text.
        myDiagram.nodeTemplate =
          $(go.Node, "Auto",
            {
                selectionObjectName: "PANEL",
                isTreeExpanded: true,
                isTreeLeaf: true
            },
            // the node's outer shape, which will surround the text
              $(go.Shape, "Circle",
                {name: "SHAPE", height: 60, fill: "whitesmoke", stroke: "black" },
                new go.Binding("fill", "color", function (dist) {
                    //dist = Math.min(blues.length - 1, dist);
                    return go.Brush.randomColor();//blues[dist];
                })),
              $(go.TextBlock,
                { font: "6pt sans-serif" },
                new go.Binding("text", "text")),
            // the expand/collapse button, at the top-right corner
            $("TreeExpanderButton",
            {
                width: 20, height: 20,
                alignment: go.Spot.TopRight,
                alignmentFocus: go.Spot.TopRight
            },
            { visible: true })  // end TreeExpanderButton
          );  // end Node`

And I insert the data in it as:

        `var nodeDataArray = [];

        for (var i = 0; i < seldiagnosysgroups.options.length; i++) {
            if (seldiagnosysgroups.options[i].selected) {
                for (var j = 0; j < arr_rsSort.length; j++) {
                    item = {}
                    item["key"] = seldiagnosysgroups.options[i].value + "_" + arr_rsSort[j].group_diagnosis;
                    item["text"] = arr_rsSort[j].group_diagnosis;
                    item["cnt"] = 0;
                    item["color"] = go.Brush.randomColor();
                    item["parent"] = seldiagnosysgroups.options[i].value + "_";
                    item["share"] = 0;
                    nodeDataArray.push(item);
                }
            }
        }
      ---
      ---
      ---
      myDiagram.model = new go.TreeModel(nodeDataArray);

        myDiagram.nodes.each(function (n) {
            if (n.data.text.indexOf("Count") >= 0) {
                if (n.data.cnt < 30)
                    n.collapseTree();
            }
        })

`

This works absolutely fine and I get the diagram as:

But whenever I click on any leaf node to expand, it overlaps the new nodes on the existing nodes as:

Hence, completely messing up the diagram. So what can I do to open the leaf nodes in the blank area and not on the existing nodes?

It ought to be the case that expanding a subtree will invalidate the layout, causing the layout to be performed again. If you collapse and expand a node, does a layout happen, resulting in the nodes moving?

Thanks for the reply Walter, yes when I collapse/expand a node, it does results in other nodes moving. So how can I solve my issue?

I am unsure of what would be best. You might need to do the collapsing work only after the whole tree has been laid out. You could do that by putting that code in a “InitialLayoutCompleted” DiagramEvent listener.

But I dont have any issue while collapsing a node, I have an issue when expanding it. It explodes its child nodes at the already existing nodes, so how do I avoid this?

But the ForceDirectedLayout depends on where the nodes are before the layout happens, so if you can make sure the nodes are about where they should be when expanded, then that wil produce better results. How to make sure they are near good locations? Layout the whole tree and then collapse.

I want to make an Incremental tree just like this example :
https://gojs.net/latest/samples/incrementalTree.html

So I dont want the layout to be in a tree structure, but in exploding Incremental Tree like structure.

And by the near good locations, it just means to be near to the parent but in blank area, not overlapping any existing node.

Well, did you try what I suggested and it didn’t help?

ForceDirectedLayout does not guarantee no overlapping nodes.

Ok lemme try calling InitialLayoutCompleted.

I checked that if I dont collapse any node initially programmatically and let all the nodes expand, it still crosses some nodes over others:

I commented this code:

        //myDiagram.nodes.each(function (n) {
        //    if (n.data.text.indexOf("Count") >= 0) {
        //        if (n.data.cnt < 30)
        //            n.collapseTree();
        //    }
        //})

So it means that the issue is initially only and not after expanding any node. I added the following code and getting the alert correctly to test:

       myDiagram.addDiagramListener("InitialLayoutCompleted", function (e) {
           alert("in listener");
       });

So what should I write in this event to adjust all these nodes so that they dont cross other nodes. I dont understand what I am missing because I followed the same code that was in the sample Incremental Tree.

I don’t see any overlapping nodes in that screenshot. But I suppose there might be some in very congested areas.

I suggested that you collapse nodes in that “InitialLayoutCompleted” listener.

I tried this code but its not working and still keeping all the nodes expanded:

        myDiagram.addDiagramListener("InitialLayoutCompleted", function (e) {
            myDiagram.nodes.each(function (n) {
                if (n.data.text.indexOf("Count") >= 0) {
                    if (n.data.cnt < 30)
                        n.collapseTree();
                }
            })
        });

So you’ve debugged this and it called Node.collapseTree as often as you expected, yes? And yet all nodes remain Node.isTreeExpanded?

No its not stopping in debugging even at “myDiagram.nodes.each(function (n) {”

Just above you said that you did get the alert call.

Whenever the Diagram.model is replaced, you will get an “InitialLayoutCompleted” DiagramEvent.

Alright, I replaced my older code to this one:

        myDiagram.model = new go.TreeModel(nodeDataArray);

        myDiagram.addDiagramListener("InitialLayoutCompleted", function (e) {
            //alert("In listener");
            myDiagram.nodes.each(function (n) {
                if (n.data.text.indexOf("Count") >= 0) {
                    if (n.data.cnt < 30)
                        n.collapseTree();
                }
            })
        });

And yes, everything is working in this event.
But now, when I click to expand any node, I get only arrows and not the shape and text of any newly visible node as:

I have to do some more work on the nodes after this code also, so may be there is issue in it also, lemme paste the full code:

       myDiagram.model = new go.TreeModel(nodeDataArray);

        myDiagram.addDiagramListener("InitialLayoutCompleted", function (e) {
            //alert("In listener");
            myDiagram.nodes.each(function (n) {
                if (n.data.text.indexOf("Count") >= 0) {
                    if (n.data.cnt < 30)
                        n.collapseTree();
                }
            })
        });

        //updateCountofListTreeValues
        var NodeKey;
        var count;
        var arrNodeKeysToBeDeleted = [];
        var arrNodes = myDiagram.model.nodeDataArray;

        for (var i = arrNodes.length - 1; i >= 0; i--) {
            var NodeKey = arrNodes[i].key;
            var nd = myDiagram.findNodeForKey(NodeKey);

            if ((NodeKey.split("_").length - 1) < 3) {
                count = 0;
                nd.findTreeChildrenNodes().each(function (ndc) {
                    count += ndc.data.cnt;
                });

                if (count == 0) {
                    arrNodeKeysToBeDeleted.push(NodeKey);
                }
                else {
                    var model = myDiagram.model;
                    model.startTransaction("changed text");

                    model.setDataProperty(nd.data, "text", nd.data.text + " (Count : " + count + ")");
                    model.setDataProperty(nd.data, "cnt", count);                        

                    model.commitTransaction("changed text");
                }
            }
        }
        for (var i = 0; i < arrNodeKeysToBeDeleted.length; i++) {
            var node = myDiagram.findNodeForKey(arrNodeKeysToBeDeleted[i]);
            myDiagram.startTransaction("deleted node");
            myDiagram.remove(node);
            myDiagram.commitTransaction("deleted node");
        }

        //UpdateSharePercentage
        var arrNodesNew = myDiagram.model.nodeDataArray;
        var percentage;
        for (var i = arrNodes.length - 1; i >= 0; i--) {
            var NodeKey = arrNodes[i].key;
            var nd = myDiagram.findNodeForKey(NodeKey);

            if (nd.data.parent == "-1")
                percentage = 100;
            else {
                var ndParent = myDiagram.findNodeForKey(nd.data.parent);
                percentage = 100 * nd.data.cnt / ndParent.data.cnt;
            }

            var model = myDiagram.model;
            model.startTransaction("changed text");

            model.setDataProperty(nd.data, "text", nd.data.text + ", Share : " + percentage.toFixed(1) + "%");
            model.setDataProperty(nd.data, "share", percentage.toFixed(1));

            model.commitTransaction("changed text");
        }

        myDiagram.nodes.each(function (n) {
            n.findObject('SHAPE').height = n.data.share * 150 / 100;
        })

Could you please just let me know what I am doing wrong in following the sample incremental tree code, I exactly want whatever is in that sample https://gojs.net/latest/samples/incrementalTree.html

That’s odd – we’ll take a closer look at your code when we get a chance.

In the meantime I did notice that you should not conduct transactions inside loops. There should be just one transaction that should include all of the changes that you want to make.

ok Thanks Walter, ok I will change that piece of code.

I think you really need to completely initialize the Diagram before you set Diagram.model. Think of the typical scenario where one repeatedly loads new diagrams on command by somehow getting a model (perhaps via Model.fromJson) and then replacing the Diagram.model.

I think you really ought to set up all of the node data objects, with their correct desired properties, before you create a model with that Array of data. If you do that there’s no need for any transactions in your model-building code. And you can use a Binding on your Shape.height property, instead of writing the code to do it. I’m guessing that might explain why those expanded nodes have zero height – because your code executed at the wrong time.

Could you please explain this by a code snippet? That would be so helpful for me, actually I am totally stuck at this point.