Subtree collapse results in orphaned nodes without link to its parent

There are no chain nodes and each node has got a single parent too. Please let me know if this can be fixed

In your screenshot the gray “Acquiring Institution Identification Code” node has two parent nodes, so the graph is not a tree.

How is the sample that I referenced not helpful?

Thanks walter , i tried that example but somehow it is not working ( not sure whether i am doing something wrong , i m using angular 7) Also i got around 1000+ nodes arranged in 3 tree layout structure , this is my base node contain 3 tree root nodes
image
expanding upwards downwards and to right . multiple parent is coming only for the upwards tree . i choose tree layout because i read that it is much faster than other layout and it perfectly fit my requirement.
i observed that if i collapse ‘Xpress transaction manual R19’ node and then expand it , then all nodes from it realign correctly . So is there any other way to accomplish this ?

I’m sorry but I am unable to understand what your situation is and what the problem is.

sorry if i am not clear , my problem is explained in post 1 where nodes in tree layout with multiple parents (links) is not getting collapsed properly and retain their original position.Now coming to original problem

initial position after expanding node - Acquiring institution identification code (orange color)

after collapsing Acquiring institution identification code (orange color) , see the orphaned node (grey color) which is not retaining to original position

grey color node will retain their original position if collapse and then expand its parent node Xpress Transaction Manual R19.1(DCI) (grey color)

I tried changing to layereddigraph and implement a button instead of tree expanded button but nodes are not getting expanded or collapsed. I want to use tree layout rather than layeredigraph because of performance issues ( contain 1000 nodes ).

The desired behavior is still ambiguous to me. Did you want the gray “Acquiring Institution Identification Code” node (AIIC) to be invisible when either or both of the parent nodes (the orange AIIC and “Xpress Transaction Manual R19.1 DCI” (“XTM”)) are collapsed? Or only when both the orange AIIC and XTM are collapsed? Or did you want some other criteria for determining the gray AIIC node’s visibility?

The behavior of the “Button” in Different Criteria for Hiding "Children" of Collapsed Nodes, replacing the “TreeExpanderButton”, is what most people want when the graph is not tree-structured.

My problem is as you can see from screenshot 2, Gray AIIC is staying at the top without any link to its parent “Xpress Transaction Manual R19 DCI” after collapsing orange “AICC”. Also it is not staying horizontally among other children of “Xpress Transaction Manual” . I am trying with the sample , will update you

i tried the example and implemented a custom tree expander button and able to expand and collapse the nodes. it works perfectly for expanding but still the problem exists while collapsing .
Gray & orange Acquiring institution identification code are similar and are present in Xpress Transaction manual and Authorization Interface (Parent nodes in below screenshot) . so i am showing both gray and orange AIIC are linked. if i expand orange AIIC then everything is working as expected


if i collapse orange AIIC , then orange AICC is visible but gray AIIC is not visible and that node itself is missing from 'Xpress transaction manual ’ ( in custom tree expander code given in example, we are changing visibility of linked nodes to false ) .

i tried to change visibility to true, but in that case node is appearing as orphaned without any links as shown below
image

is there a way to still view gray AIIC node and keep it in same row of other children of ‘Xpress Transaction manual’

In your first screenshot, the XTM node is collapsed, yet all of its children are visible. This is inconsistent. Same problem in second screenshot.

If you want to make a node visible, you also need to control which connected links should be visible.

Could you please write a table showing all four combinations of the orange AIIC node and the XTM node collapsed state and the desired visibility of the gray AIIC node? Basically you need to change the collapseFrom and expandFrom functions to do what you want.

Thanks for helping me out in this . I corrected my code and now one last question , how can i align a node along with other nodes while collapsing .
check this screenshot . check the position of “Track 1 Data” .

after expanding Track 1 Data which is child of ‘Authorization Interface R19’

As you can see Track 1 data(child of XTM) is residing on top of Track 1 Data (child of ‘Authorization Interface R19’ ). My question is how can i move Track 1 data(child of XTM) to its original position , ie align with other gray children of XTM. I want to achieve this in collapseFrom () function Track 1 Data (child of ‘Authorization Interface R19’ )

I think the TreeLayout happens to see the link from “Track 1 Data” (T1D1) to “Track 1 Data” (T1D2) first, and thus treats those two nodes as parent and child. Later it happens to see the link from XTM to T1D2, but since it knows that T1D2 is already a child of T1D1, it ignores that link.

If you know that the layout should ignore the link from T1D1 to T1D2, then you can set Link.isLayoutPositioned to false on that link and leave Link.isLayoutPositioned true on the link from XTM to T1D2.

Alternatively, if you do not want to treat T1D2 as a child of T1D1, not only for TreeLayout but also for other “Tree” navigation by the methods of Node, then set Link.isTreeLink to false on that link and true on the link from XTM to T1D2.

Thanks Walter . I tried both combination but it is not working , with these methods, T1D1 position is always on top of the T1D2 and it is not getting aligned with other child nodes to XTM. I observed that if collapse and then expand XTM then it is T1D1 is aligning properly with other nodes . Is that something can be done programmatic, say collapse and then expand a node in the button click of custom tree expander button?

Adding 1 more point , nodes will align correctly if i expand another node , this is the case with default tree expander button

After expanding and collapsing Orange Acquiring institution identification code node

if we expand other node say Orange function code , then Acquiring institution identification code retains its original postion

This is case for other nodes also , All nodes will realign correctly if i collapse and then expand Xpress transaction manual(parent node of all gray nodes). so can we can simulate the same behavior somehow in a button click

Do not use the “TreeExpanderButton” – it uses the Node.isTreeExpanded property and Node.collapseTree() and Node.expandTree(), which follow the standard policies for collapsing/expanding subtrees. And those policies are not what you seem to want.

Use the plain “Button” that we provide in that custom sample. If you want different behavior, modify its collapseFrom and expandFrom functions.

i did tried that example but as explained Subtree collapse results in orphaned nodes without link to its parent - #14 by nibinki , setting isTreeLink and isLayoutPositioned is not working , So i was thinking why aligning happens if we exapand other nodes and root nodes. Is there any other way we can fix this

Here’s the Custom Expand Collapse sample again. The only changes I have made are to the model data and to the direction of the LayeredDigraphLayout.

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Different Criteria for Hiding "Children" of Collapsed Nodes</title>
  <meta name="description" content="Custom policy for collapsing and expanding subtrees, different than TreeExpanderButton." />
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- Copyright 1998-2019 by Northwoods Software Corporation. -->

  <script src="go.js"></script>
  <script src="../assets/js/goSamples.js"></script>  <!-- this is only for the GoJS Samples framework -->
  <script id="code">
    function init() {
      if (window.goSamples) goSamples();  // init for these samples -- you don't need to call this
      var $ = go.GraphObject.make;

      myDiagram =
        $(go.Diagram, "myDiagramDiv",
          {
            padding: 10,
            layout: $(go.LayeredDigraphLayout,
              { direction: 270, layeringOption: go.LayeredDigraphLayout.LayerLongestPathSource }),
            "undoManager.isEnabled": true
          });

      myDiagram.nodeTemplate =
        $(go.Node, go.Panel.Vertical,
          { portId: "", fromLinkable: true, toLinkable: true },
          new go.Binding("visible"),
          $(go.Panel, go.Panel.Auto,
            $(go.Shape,
              { fill: "white", minSize: new go.Size(30, 30), strokeWidth: 0 },
              { cursor: "pointer" },  // indicate that linking may start here
              new go.Binding("fill", "color")),
            $(go.TextBlock,
              { margin: 2 },
              { fromLinkable: false, toLinkable: false },  // don't start drawing a link from the text
              new go.Binding("text", "key"))),
          $("Button",  // a replacement for "TreeExpanderButton" that works for non-tree-structured graphs
            // 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(),
            $(go.Shape,
              {
                name: "ButtonIcon",
                figure: "MinusLine",
                desiredSize: new go.Size(6, 6)
              },
              new go.Binding("figure", "isCollapsed",  // data.isCollapsed remembers "collapsed" or "expanded"
                function(collapsed) { return collapsed ? "PlusLine" : "MinusLine"; })),
            {
              click: function(e, obj) {
                e.diagram.startTransaction();
                var node = obj.part;
                if (node.data.isCollapsed) {
                  expandFrom(node, node);
                } else {
                  collapseFrom(node, node);
                }
                e.diagram.commitTransaction("toggled visibility of dependencies");
              }
            })
        );

      function collapseFrom(node, start) {
        if (node.data.isCollapsed) return;
        node.diagram.model.setDataProperty(node.data, "isCollapsed", true);
        if (node !== start) node.diagram.model.setDataProperty(node.data, "visible", false);
        node.findNodesOutOf().each(collapseFrom);
      }

      function expandFrom(node, start) {
        if (!node.data.isCollapsed) return;
        node.diagram.model.setDataProperty(node.data, "isCollapsed", false);
        if (node !== start) node.diagram.model.setDataProperty(node.data, "visible", true);
        node.findNodesOutOf().each(expandFrom);
      }

      myDiagram.linkTemplate =
        $(go.Link,
          { relinkableFrom: true, relinkableTo: true, corner: 10 },
          $(go.Shape),
          $(go.Shape, { toArrow: "Standard" }));

      myDiagram.model = new go.GraphLinksModel([
        { key: "B", color: "gray" },
        { key: "C", color: "orange" },
        { key: "D0", color: "gray" },
        { key: "D1", color: "orange" },
        { key: "D2", color: "orange" },
        { key: "D3", color: "gray" },
      ], [
          { from: "B", to: "D0" },
          { from: "B", to: "D3" },
          { from: "C", to: "D1" },
          { from: "D1", to: "D3" },
          { from: "C", to: "D2" }
        ]);
    }
  </script>
</head>
<body onload="init()">
  <div id="sample">
    <div id="myDiagramDiv" style="border: solid 1px black; width:600px; height:700px"></div>
    <p>
      The "TreeExpanderButton", which changes the <a>Node.isTreeExpanded</a> property, really only works with tree structures.
      When you want to hide/show the "downstream" nodes from a given node, using the "TreeExpanderButton" might not do what you like,
      especially when there are cycles in the graph structure.
    </p>
    <p>
      Instead, this sample implements a "Button" with custom behavior to modify the visibility of each Node.
      If this behavior is still not quite right for your app, you can adapt the behavior implemented in the
      <pre>collapseFrom</pre> and <pre>expandFrom</pre> functions to use different criteria for when to stop recursion.
    </p>
  </div>
</body>
</html>

As far as I can tell, this operates exactly as I believe you are asking for.

is there a way to keep the root rode of layereddigraph to be fixed just like in tree layout arrangement: go.TreeLayout.ArrangementFixedRoots. if i change my layout, then it is pushing downwards in a group layout

Oh, right – I forgot you were using TreeLayout. OK, position the root nodes to be where you want them to stay, and use this for your Diagram.layout:

        layout: $(go.TreeLayout,
          { angle: 270, arrangement: go.TreeLayout.ArrangementFixedRoots }),

Thanks walter , really appreciate your time and effort . I noticed that in your example , all nodes are expanded initially . But my requirement is not to expand nodes initially because of 200+ nodes . I just added { isTreeExpanded: false } condition in nodeTemplate to make all nodes collapse initially and then none of the nodes are getting expanded on click of button!!!. I debugged expandForm() function , isCollapsed and visible is getting changed but nodes are not getting expanded. Do we need to call cmd.expandTree() function ? Please help

function init() {
if (window.goSamples) goSamples(); // init for these samples – you don’t need to call this
var $ = go.GraphObject.make;

  myDiagram =
    $(go.Diagram, "myDiagramDiv",
      {
        padding: 10,
        layout: $(go.LayeredDigraphLayout,
        { direction: 270, layeringOption: go.LayeredDigraphLayout.LayerLongestPathSource }),
		   //layout: $(go.TreeLayout,
             //   { angle: 270, arrangement: go.TreeLayout.ArrangementFixedRoots }),
        "undoManager.isEnabled": true
      });

  myDiagram.nodeTemplate =
    $(go.Node, go.Panel.Vertical,
	          { portId: "", fromLinkable: true, toLinkable: true },

      new go.Binding("visible"),
            { isTreeExpanded: false },

      $(go.Panel, go.Panel.Auto,
        $(go.Shape,
          { fill: "white", minSize: new go.Size(30, 30), strokeWidth: 0 },
          { cursor: "pointer" },  // indicate that linking may start here
          new go.Binding("fill", "color")),
        $(go.TextBlock,
          { margin: 2 },
          { fromLinkable: false, toLinkable: false },  // don't start drawing a link from the text
          new go.Binding("text", "key"))),

      $("Button",  // a replacement for "TreeExpanderButton" that works for non-tree-structured graphs
        // 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(),
        $(go.Shape,
          {
            name: "ButtonIcon",
            figure: "MinusLine",
            desiredSize: new go.Size(6, 6)
          },
          new go.Binding("figure", "isCollapsed",  // data.isCollapsed remembers "collapsed" or "expanded"
            function(collapsed) { return collapsed ? "PlusLine" : "MinusLine"; })),
        {
          click: function(e, obj) {
            e.diagram.startTransaction();
            var node = obj.part;
            if (node.data.isCollapsed) {
              expandFrom(node, node);
            } else {
              collapseFrom(node, node);
            }
            e.diagram.commitTransaction("toggled visibility of dependencies");
          }
        })
    );

  function collapseFrom(node, start) {
    if (node.data.isCollapsed) return;
    node.diagram.model.setDataProperty(node.data, "isCollapsed", true);
    if (node !== start) node.diagram.model.setDataProperty(node.data, "visible", false);
    node.findNodesOutOf().each(collapseFrom);
  }

  function expandFrom(node, start) {
  console.log(node);
  console.log(start);
    if (!node.data.isCollapsed){
	return;
	}
    node.diagram.model.setDataProperty(node.data, "isCollapsed", false);
    if (node !== start){
	node.diagram.model.setDataProperty(node.data, "visible", true);
	}
	//debugger;
    node.findNodesOutOf().each(expandFrom);
  }

  myDiagram.linkTemplate =
    $(go.Link,
	 { routing: go.Link.AvoidsNodes },
      $(go.Shape),
      $(go.Shape, { toArrow: "Standard" }));

  myDiagram.model = new go.GraphLinksModel([
    { key: "B", color: "gray", },
    { key: "C", color: "orange", },
    { key: "D0", color: "gray",  },
    { key: "D1", color: "orange",  },
    { key: "D2", color: "orange",  },
    { key: "D3", color: "gray",  },
  ], [
      { from: "B", to: "D0" },
      { from: "B", to: "D1" },
      { from: "D0", to: "C" },
      { from: "D1", to: "D2" },
      { from: "D2", to: "C" },
	  { from: "C", to: "D2" },
	  { from: "D0", to: "D3" }
    ]);
}

As I said before, if you want to implement your own criteria for expanding and collapsing nodes and associated links and nodes, you must not use Node.isTreeExpanded or any of the Node methods or other properties that involve identifying/navigating/collapsing/expanding trees. You have to implement the state on the node data (rather than use the built-in properties) and you have to implement the collapse and expand functionality to do exactly what you want. Otherwise the standard policy and your policy will get confused.

That has been done in that Custom Collapse Expand sample. It still isn’t clear to me that its policies aren’t what you want.