2nd level child comes out of Group though locations are correct

Hi,

I have Nested Groups, like Group A contains Group B. Then Group B Contains Node C or Group C.

I have a different layout for collapsed groups. Which means i have another template that indicates a collapsed layout template with different category value.

When a user uses the expanderbutton to collapse the Top Level group, i am using group.collapseGroup() as well as diagram.commandHandler.collapseGroup(group) methods.

This is happening in the transaction as well.

When i just collapse the Top Level Group i have observed the Location of Its children’s are same.
When i move the Collapsed group to some other location, i can see the diagram model JSON gets updated with new location & this happens with all of its children.

But after moving to a new location when i expand the Group using group.expandSubGraph() method & diagram.commandHandler.expandSubGraph(group) on UI it shows the 2nd level child at some different older location. I have verified the Data on that Group holds new correct location. But on the diagram, it still shows at the older location.

If i try to move a node which is out of the group then it will immediately go to its correct location. you will be able to see it in the recording too.

Also this is not happening for 1st level immediate child of Top Level group.

I enabled the GoJS Debug script, but nothing was getting logged in the console as well.

I have uploaded a recording video for easy understanding of this problem. Please review it and let me know what could be going wrong here or what should i test.

Thank you.

What are your two group templates? Does the problem happen if you do not switch templates when you expand or collapse a group?

Hi Walter,

Yes i can confirm this happens even if i don’t switch templates.

Please refer group template below.

const $ = go.GraphObject.make;
    let properties: any = {
      ungroupable: true,
      contextMenu: this.myContextMenu,
      selectionAdornmentTemplate: this.TMSelectionAdornmentTemplate(),
      // highlight when dragging into the Group
      mouseDragEnter: this.highlightGroupMouseEnterNewLayout.bind(this),
      mouseDragLeave: this.highlightGroupMouseLeave.bind(this),
      dragComputation: this.stayInGroup.bind(this),
      // when the selection is dropped into a Group, add the selected Parts into that Group;
      // if it fails, cancel the tool, rolling back any changes
      mouseDrop: this.finishDrop.bind(this),
      selectionChanged: (e: go.Part) => currentInstance.setLayerForeground(e)
    };

    let editNodeText: boolean = true;
return $(
      go.Group, "Auto", {
      layout: $(go.LayeredDigraphLayout, {
        direction: 0,
        layerSpacing: 10,
        isInitial: false,
        isOngoing: false,
        columnSpacing: 10
      }),
      computesBoundsAfterDrag: false,  
      computesBoundsIncludingLinks: false,  
      computesBoundsIncludingLocation: false, 
      handlesDragDropForMembers: true, 
      movable: true,
      isShadowed: true,
      shadowOffset: new go.Point(2, 2),
      shadowColor: "#c7c7c7"
    }, new go.Binding("movable", "AllowMove"),
      new go.Binding("location", "Location", go.Point.parse).makeTwoWay(go.Point.stringify),
      $(go.Shape, this.getShapeNewLayout("Collection"), { strokeDashArray: this.getStrokeDashArrayNewLayout("Collection"), parameter1: 7, strokeWidth: 2 },
        new go.Binding("fill", "BackgroundColor"),
        new go.Binding("stroke", "isHighlighted", function (h) { return h ? "red" : "#bababa"; }).ofObject(),
        new go.Binding("stroke", "BorderColor", function (f) { return "black" ? "#3c3b3d" : f }).ofObject(),
        new go.Binding("strokeWidth", "BorderThickness")),
      $(go.Panel, go.Panel.Table, { name: "panelRegular", defaultAlignment: go.Spot.Center },
        $(go.RowColumnDefinition, { row: 1 }),
        $(go.RowColumnDefinition, { row: 2 }),
        $(go.Panel, go.Panel.Horizontal, { row: 1, column: 0, margin: 2, stretch: go.GraphObject.Horizontal }, new go.Binding("background", "TitleBackgroundColor"),
          $("SubGraphExpanderImageButton", { alignment: go.Spot.Right, margin: 5 }),
          $(go.TextBlock, { alignment: go.Spot.LeftCenter, editable: editNodeText, margin: 5, font: "bold 18px 'Gilroy',sans-serif", portId: "T", stroke: "#3c3b3d", fromSpot: go.Spot.Default, toSpot: go.Spot.Default, toLinkable: true, fromLinkable: true, cursor: "pointer", textValidation: currentInstance.okName, textEdited: currentInstance.nodeTextEdited.bind(currentInstance) }, new go.Binding("text", "FullName").makeTwoWay()),
          currentInstance.makeInformationIcon(go.Spot.Right, go.Spot.Right, 5)),
        $(go.Placeholder, { row: 2, column: 0, margin: 2, alignment: go.Spot.TopLeft, padding: 10 }, new go.Binding("background", "BackgroundColor"))),
      properties,
      new go.Binding("isSubGraphExpanded", "isGraphExpanded").makeTwoWay());
  }

You have set Layout.isInitial and Layout.isOngoing to false on your Group.layout. So as groups are expanded, no layout is done to make sure that nodes have real locations.

Or have you made sure that every non-Group Node has a real location, probably via a Binding on the Node.location property?

Even the child looks like a node, it is actually derived from a go.Group & YES we have every nodes with Node.location binding with “Location” property of our JSON data.

I have verified the location has updated correct value after we move the top level group.

OK, here’s what I was able to construct using the code that you provided above. I had to comment out basically everything involving this, referring to functions that I know nothing about.

I’m not able to reproduce any problems. What is different in this sample? Ignore irrelevant differences, please.

<!DOCTYPE html>
<html>
<head>
  <title>Minimal GoJS Sample</title>
  <!-- Copyright 1998-2022 by Northwoods Software Corporation. -->
</head>
<body>
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
  <textarea id="mySavedModel" style="width:100%;height:250px"></textarea>

  <script src="https://unpkg.com/gojs"></script>
  <script id="code">
const $ = go.GraphObject.make;

const myDiagram =
  $(go.Diagram, "myDiagramDiv",
    {
      "undoManager.isEnabled": true,
      "ModelChanged": e => {     // just for demonstration purposes,
        if (e.isTransactionFinished) {  // show the model data in the page's TextArea
          document.getElementById("mySavedModel").textContent = e.model.toJson();
        }
      }
    });

myDiagram.nodeTemplate =
  $(go.Node, "Auto",
    new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
    $(go.Shape,
      { fill: "white" },
      new go.Binding("fill", "color")),
    $(go.TextBlock,
      { margin: 8 },
      new go.Binding("text"))
  );

    let properties = {
      ungroupable: true,
      //contextMenu: this.myContextMenu,
      //selectionAdornmentTemplate: this.TMSelectionAdornmentTemplate(),
      // highlight when dragging into the Group
      //mouseDragEnter: this.highlightGroupMouseEnterNewLayout.bind(this),
      //mouseDragLeave: this.highlightGroupMouseLeave.bind(this),
      //dragComputation: this.stayInGroup.bind(this),
      // when the selection is dropped into a Group, add the selected Parts into that Group;
      // if it fails, cancel the tool, rolling back any changes
      //mouseDrop: this.finishDrop.bind(this),
      //selectionChanged: (e: go.Part) => currentInstance.setLayerForeground(e)
    };

    let editNodeText = true;

myDiagram.groupTemplate =
  $(go.Group, "Auto", {
      layout: $(go.LayeredDigraphLayout, {
        direction: 0,
        layerSpacing: 10,
        isInitial: false,
        isOngoing: false,
        columnSpacing: 10
      }),
      computesBoundsAfterDrag: false,  
      computesBoundsIncludingLinks: false,  
      computesBoundsIncludingLocation: false, 
      handlesDragDropForMembers: true, 
      movable: true,
      isShadowed: true,
      shadowOffset: new go.Point(2, 2),
      shadowColor: "#c7c7c7"
    }, new go.Binding("movable", "AllowMove"),
      new go.Binding("location", "Location", go.Point.parse).makeTwoWay(go.Point.stringify),
      $(go.Shape, //this.getShapeNewLayout("Collection"),
        { //strokeDashArray: this.getStrokeDashArrayNewLayout("Collection"), 
          fill: "transparent",
          parameter1: 7, strokeWidth: 2 },
        new go.Binding("fill", "BackgroundColor"),
        new go.Binding("stroke", "isHighlighted", function (h) { return h ? "red" : "#bababa"; }).ofObject(),
        new go.Binding("stroke", "BorderColor", function (f) { return "black" ? "#3c3b3d" : f }).ofObject(),
        new go.Binding("strokeWidth", "BorderThickness")),
      $(go.Panel, go.Panel.Table, { name: "panelRegular", defaultAlignment: go.Spot.Center },
        $(go.RowColumnDefinition, { row: 1 }),
        $(go.RowColumnDefinition, { row: 2 }),
        $(go.Panel, go.Panel.Horizontal,
          { row: 1, column: 0, margin: 2, stretch: go.GraphObject.Horizontal, background: "lavender" },
          new go.Binding("background", "TitleBackgroundColor"),
          $("SubGraphExpanderButton", //"SubGraphExpanderImageButton",
            { alignment: go.Spot.Right, margin: 5 }),
          $(go.TextBlock,
            {
              alignment: go.Spot.LeftCenter,
              editable: editNodeText,
              margin: 5,
              font: "bold 18px 'Gilroy',sans-serif",
              portId: "T",
              stroke: "#3c3b3d",
              fromSpot: go.Spot.Default,
              toSpot: go.Spot.Default,
              //toLinkable: true,
              //fromLinkable: true,
              //cursor: "pointer",
              //textValidation: currentInstance.okName,
              //textEdited: currentInstance.nodeTextEdited.bind(currentInstance)
            }, new go.Binding("text" /*, "FullName"*/).makeTwoWay()),
          //currentInstance.makeInformationIcon(go.Spot.Right, go.Spot.Right, 5)
          ),
        $(go.Placeholder,
          { row: 2, column: 0, margin: 2, alignment: go.Spot.TopLeft, padding: 10 },
          new go.Binding("background", "BackgroundColor"))),
      properties,
      new go.Binding("isSubGraphExpanded", "isGraphExpanded").makeTwoWay()
    );

myDiagram.model = new go.GraphLinksModel(
  [
    {"key":"GA","text":"Group A","isGroup":true,"Location":"3 36.5", "isGraphExpanded":false},
    {"key":"GB","text":"Group B","isGroup":true,"group":"GA","Location":"96 83", "isGraphExpanded":false},
    {"key":"GC","text":"Group C","isGroup":true,"group":"GB","Location":"109 129.5", "isGraphExpanded":false},
    {"key":1,"text":"Alpha","color":"lightblue","group":"GA","loc":"13 136.125"},
    {"key":2,"text":"Beta","color":"orange","group":"GB","loc":"252.02284749830795 192.75"},
    {"key":3,"text":"Gamma","color":"lightgreen","group":"GC","loc":"119 139.5"},
    {"key":4,"text":"Delta","color":"pink","group":"GB","loc":"250.52284749830795 142.75"}
  ],
  [
    { from: 1, to: 2 },
    { from: 1, to: 3 },
    { from: 3, to: 4 },
  ]);
  </script>
</body>
</html>

Hi Walter,

We are currently using 2.1.41 version and facing this issue.

Just now I updated the package to 2.2.14 & I can see the issue is resolved even without making a single line of code change.
Previously we were using 1.8.29 in that also we weren’t facing the issue.
Could it be version specific issue?

What do you suggest, should we keep using this latest package or there is some fix we can apply to our existing version?

Is there any reason not to use the latest version?