Group objects and auto layout

We have run into various issues using group objects. We have reviewed a few posts but doesn’t seem like there was any solution.

  1. are using group objects to group nodes of certain type as shown in the attached screen shot. We had disabled auto layout because it was affecting the connections (arrows) showing as disconnected when we collapsed group objects and expanded. Is it possible to keep auto layout functionality AND auto layout for the connections (arrows) ?

  2. Also when a node that is outside the group is placed on the canvas and THEN the group object is expanded (as it was collapsed earlier), it overlaps the node that’s outside the group

  3. Akso see the 3rd image pasted - the arrows are shown as disconnected when we refresh the browser screen. Once we move the group pbject they are shown properly but immediately after the refresh, the arrows are al shown as disconnected.

image
image

Thank You

1 and 3. Were the groups initially loaded collapsed? Do you have a Binding on the Link.points property? What is your Group.layout? Although even that doesn’t really explain the routing behavior that you are seeing, for which I cannot provide an explanation. Please provide more information so that we can reproduce the problem.

Normally the routing of links is performed by the link itself, without the need for any layout. However, some layouts also route their links, which often takes precedence over the natural routing that links do. So the disablement of the layout(s) would not prevent the link from routing itself so that it correctly connects its two ports.

  1. What is your Diagram.layout? If that layout normally ensures that the nodes do not overlap, then disabling its automatic layout behavior would naturally cause the behavior that you are seeing.

Here’s an example:

<!DOCTYPE html>
<html>
<head>
  <title>Simple Shifting of Nodes to Avoid Overlapping with Groups</title>
  <!-- Copyright 1998-2021 by Northwoods Software Corporation. -->
</head>
<body>
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
  After each layout, the <code>shiftNodes</code> function is called on each group to make sure no
  nodes overlap with the group.  The function moves overlapping nodes rightwards or downwards or both.
  A layout normally happens whenever any node changes size, so groups do not need to be expanded
  in order for any overlapping nodes to be shifted.
  <textarea id="mySavedModel" style="width:100%;height:250px"></textarea>

  <script src="https://unpkg.com/gojs"></script>
  <script id="code">
function init() {
  const $ = go.GraphObject.make;
  myDiagram =
    $(go.Diagram, "myDiagramDiv",
      {
        "LayoutCompleted": e => { e.diagram.findTopLevelGroups().each(shiftNodes); },
        "undoManager.isEnabled": true,
        "ModelChanged": e => {
          if (e.isTransactionFinished) document.getElementById("mySavedModel").textContent = e.model.toJson();
        }
      });

  function makePort(name, spot) {
    return $(go.Shape, "Circle",
      { width: 6, height: 6, strokeWidth: 0, fill: "green",
        alignment: spot, alignmentFocus: spot.opposite() },
      { portId: name, fromLinkable: true, toLinkable: true,
        fromSpot: spot, toSpot: spot });
  }

  myDiagram.nodeTemplate =
    $(go.Node, "Spot",
      new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
      $(go.Panel, "Auto",
        { width: 100, height: 80 },
        $(go.Shape, { fill: "lightgreen" }),
        $(go.TextBlock, { editable: true },
          new go.Binding("text").makeTwoWay())
      ),
      makePort("T", go.Spot.Top),
      makePort("R", go.Spot.Right),
      makePort("B", go.Spot.Bottom),
      makePort("L", go.Spot.Left),
    );

  myDiagram.groupTemplate =
    $(go.Group, "Vertical",  // title above Placeholder
      new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
      new go.Binding("isSubGraphExpanded").makeTwoWay(),
      { background: "transparent" },
      $(go.Panel, "Horizontal",  // button next to TextBlock
        { stretch: go.GraphObject.Horizontal },
        $("SubGraphExpanderButton",
          { alignment: go.Spot.Right, margin: 5 }),
        $(go.TextBlock,
          {
            alignment: go.Spot.Left,
            editable: true,
            margin: 5
          },
          new go.Binding("text").makeTwoWay())
      ),  // end Horizontal Panel
      $(go.Panel, "Auto",
        { alignment: go.Spot.Left },
        $(go.Shape, { fill: "whitesmoke" }),
        $(go.Placeholder,
          { padding: 10 })
      )
    );

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

  // This function moves any nodes that overlap with the given Part, either rightwards or downwards.
  // Currently it is only called on Groups, but it could work for any Node.
  function shiftNodes(part) {
    const diagram = part.diagram;
    if (diagram === null) return;
    part.ensureBounds();
    const b = part.actualBounds;
    const overlaps = diagram.findObjectsIn(b,
          x => { const p = x.part; return (p.isTopLevel && p instanceof go.Node) ? p : null; },
          node => node !== part && !node.isMemberOf(part),
          true);
    let dx = 0;
    let dy = 0;
    const shiftsXY = new go.Set();
    const shiftsX = new go.Set();
    const shiftsY = new go.Set();
    overlaps.each(node => {
      const r = node.actualBounds;
      if (r.contains(b.right, b.bottom)) {
        dx = Math.max(dx, b.right - r.left);
        dy = Math.max(dy, b.bottom - r.top);
        shiftsXY.add(node);
      } else if (b.contains(r.left, r.bottom)) {
        dx = Math.max(dx, b.right - r.left);
        shiftsX.add(node);
      } else if (b.contains(r.right, r.top)) {
        dy = Math.max(dy, b.bottom - r.top);
        shiftsY.add(node);
      }
    });
    if (dx > 0) diagram.moveParts(shiftsX, new go.Point(dx+10, 0), false);
    if (dy > 0) diagram.moveParts(shiftsY, new go.Point(0, dy+10), false);
    if (dx > 0 && dy > 0) diagram.moveParts(shiftsXY, new go.Point(dx+10, dy+10), false);
  }


  myDiagram.model = $(go.GraphLinksModel,
    {
      linkFromPortIdProperty: "fpid",
      linkToPortIdProperty: "tpid",
      nodeDataArray:
        [
          { key: 0, isGroup: true, text: "Group", isSubGraphExpanded: false, loc: "0 0" },
          { key: 1, text: "Alpha", group: 0, loc: "10 10" },
          { key: 2, text: "Beta", group: 0, loc: "180 40" },
          { key: 3, text: "Gamma", group: 0, loc: "60 130" },
          { key: 4, text: "Delta", loc: "200 70" }  // in the way when the group is expanded
        ],
      linkDataArray:
        [
          { from: 1, fpid: "R", to: 2, tpid: "L" },
          { from: 1, fpid: "B", to: 3, tpid: "T" }
        ]
    });
}
window.addEventListener('DOMContentLoaded', init);
  </script>
</body>
</html>

thanks. will review and let you know.