I notice your Group template doesn’t have a Node.location binding, which is useful for when the Group is collapsed, since then its Placeholder won’t be telling it where to be located.
But with or without such a binding, I cannot reproduce an exception. Here’s my code using your group template and link template. (I simplified the bindings involving your “Colors” class – that shouldn’t matter.)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover"/>
<meta name="description" content="Nodes with varying lists of ports on each of four sides."/>
<!-- Copyright 1998-2025 by Northwoods Software Corporation. -->
<title>Simple AvoidsNodes Routing Test</title>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/gojs/release/go-debug.js"></script>
<script id="code">
function init() {
myDiagram =
new go.Diagram("myDiagramDiv", { //Diagram refers to its DIV HTML element by id
"animationManager.isEnabled": false,
"undoManager.isEnabled": true
});
const $ = go.GraphObject.make;
// the node template
// includes a panel on each side with an itemArray of panels containing ports
myDiagram.nodeTemplate =
new go.Node("Table", {
locationObjectName: "BODY",
locationSpot: go.Spot.Center,
selectionObjectName: "BODY"
})
.bindTwoWay("location", "loc", go.Point.parse, go.Point.stringifyFixed(1))
.add(
// the body
new go.Panel("Auto", {
row: 1, column: 1, name: "BODY",
stretch: go.Stretch.Fill
})
.add(
new go.Shape("Rectangle", {
fill: "#dbf6cb", stroke: null, strokeWidth: 0,
minSize: new go.Size(60, 60)
}),
new go.TextBlock({ margin: 10, textAlign: "center",
font: "bold 14px Segoe UI,sans-serif", stroke: "#484848",
editable: true
})
.bindTwoWay("text", "name")
), // end Auto Panel body
); // end Node
// an orthogonal link template, reshapable and relinkable
myDiagram.linkTemplate =
$(go.Link, {
routing: go.Routing.AvoidsNodes,
corner: 5,
relinkableFrom: true,
relinkableTo: true,
},
$(go.Shape, {
name: 'linkLine',
},
new go.Binding('stroke', 'isHighlighted', function (h) {
return h ? "dodgerblue" : "black"
}).ofObject(),
{ strokeWidth: 3 },
),
$(go.Shape, {
name: 'linkArrow',
},
new go.Binding('stroke', 'isHighlighted', function (h) {
return h ? "dodgerblue" : "black"
}).ofObject(),
new go.Binding('fill', 'isHighlighted', function (h) {
return h ? "dodgerblue" : "black"
}).ofObject(),
{ toArrow: 'Standard' },
),
);
myDiagram.groupTemplate =
$(go.Group, 'Auto', {
isSubGraphExpanded: false,
},
new go.Binding('location', 'loc', go.Point.parse, go.Point.stringify),
new go.Binding('background', 'isHighlighted', function (h) {
return h ? '#dedede' : 'transparent'
}).ofObject(),
$(go.Shape, 'Rectangle', { fill: null, stroke: "blue", strokeWidth: 2 }),
$(go.Panel, 'Vertical', // title above Placeholder
$(go.Panel, 'Horizontal', // button next to TextBlock
{ stretch: go.GraphObject.Horizontal, background: "lavender" },
$('SubGraphExpanderButton', { alignment: go.Spot.Right, margin: 5 }),
$(go.TextBlock, {
alignment: go.Spot.Left,
editable: true,
margin: new go.Margin(5, 15, 5, 5),
font: 'bold 14px roboto, normal',
//opacity:0.75,
stroke: '#ffffff',
},
new go.Binding('text', 'label').makeTwoWay(),
),
), // end Horizontal Panel
$(go.Placeholder, { padding: 5, alignment: go.Spot.TopLeft }),
) // end Vertical Panel
); // end Group and call to add to template Map
// load the diagram from JSON data
load();
}
// Save the model to / load it from JSON text shown on the page itself, not in a database.
function save() {
document.getElementById("mySavedModel").value = myDiagram.model.toJson();
myDiagram.isModified = false;
}
function load() {
myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").value);
}
window.addEventListener('DOMContentLoaded', init);
</script>
<div id="myDiagramDiv" style="width:600px; height:500px; border:1px solid black"></div>
<div>
<div>
<button id="SaveButton" onclick="save()">Save</button>
<button onclick="load()">Load</button>
Diagram Model saved in JSON format:
</div>
<textarea id="mySavedModel" style="width:100%;height:250px">
{ "class": "GraphLinksModel",
"copiesArrays": true,
"copiesArrayObjects": true,
"pointsDigits": 1,
"linkFromPortIdProperty": "fromPort",
"linkToPortIdProperty": "toPort",
"nodeDataArray": [
{"key":1,"name":"Unit One","loc":"88.3 207.2"},
{"key":2,"name":"Unit Two","loc":"395.9 189.3"},
{"key":3,"name":"Unit Three","loc":"227.8 198.9","group":5},
{"key":5,"isGroup":true,"name":"a Group","loc":"178.18158035278321 163.9","isSubGraphExpanded":false}
],
"linkDataArray": [
{"from":1,"to":2,"fromPort":"right0","toPort":"left1"},
{"from":1,"to":2,"fromPort":"right1","toPort":"left2"}
]}
</textarea>
</div>
</body>
</html>
Oh, the “$” isn’t jQuery – it’s a GraphObject.make abbreviation.