Here’s a demonstration of not saving the end points (first and last points) of the link route in the model when using a TwoWay Binding on the Link.points property.
<!DOCTYPE html>
<html>
<head>
<title>Not saving endpoints in "points" Array</title>
<!-- Copyright 1998-2025 by Northwoods Software Corporation. -->
</head>
<body>
<div style="width: 100%; display: flex; justify-content: space-between">
<div style="display: flex; flex-direction: column; margin: 0 2px 0 0">
<div id="myPaletteDiv" style="flex-grow: 1; width: 140px; background-color: floralwhite; border: solid 1px black"></div>
<div id="myOverviewDiv" style="margin: 2px 0 0 0; width: 140px; height: 100px; background-color: whitesmoke; border: solid 1px black"></div>
</div>
<div id="myDiagramDiv" style="flex-grow: 1; height: 400px; border: solid 1px black"></div>
</div>
<div>
<button id="myLoadButton">Load</button>
<button id="mySaveButton">Save</button>
</div>
<textarea id="mySavedModel" style="width:100%;height:200px">
{ "class": "GraphLinksModel",
"pointsDigits": 1,
"nodeDataArray": [
{"key":1,"text":"hello","color":"green","location":"-20 40"},
{"key":2,"text":"world","color":"red","location":"160 80"}
],
"linkDataArray": [{"from":1,"to":2,"points":[10.2,40.0,42.2,40.0,42.2,-17.3,110.6,-17.3,110.6,80.0,128.0,80.0]}]}
</textarea>
<script src="https://cdn.jsdelivr.net/npm/gojs/release/go-debug.js"></script>
<script id="code">
// initialize main Diagram
const myDiagram =
new go.Diagram("myDiagramDiv", {
"draggingTool.isGridSnapEnabled": true,
"commandHandler.archetypeGroupData": { isGroup: true, text: "Group", color: "green" },
"undoManager.isEnabled": true
});
myDiagram.nodeTemplate =
new go.Node("Auto", { locationSpot: go.Spot.Center })
.bindTwoWay("location", "location", go.Point.parse, go.Point.stringify)
.add(
new go.Shape({
fill: "white", stroke: "gray", strokeWidth: 2,
portId: "", fromLinkable: true, toLinkable: true,
fromLinkableDuplicates: true, toLinkableDuplicates: true,
fromLinkableSelfNode: true, toLinkableSelfNode: true
})
.bind("stroke", "color"),
new go.TextBlock({
margin: new go.Margin(5, 5, 3, 5), font: "10pt sans-serif",
minSize: new go.Size(16, 16), maxSize: new go.Size(120, NaN),
editable: true
})
.bindTwoWay("text")
);
myDiagram.linkTemplate =
new go.Link({
routing: go.Routing.Orthogonal,
reshapable: true, resegmentable: true
})
.bindTwoWay("points", "points", loadPoints, savePoints)
.add(new go.Shape({ strokeWidth: 2 }));
function loadPoints(list, link) {
if (list.length >= 1) {
const fromport = link.fromPort;
const toport = link.toPort;
if (fromport !== null && toport !== null) {
const ortho = link.isOrthogonal;
const fromnode = link.fromNode;
fromnode.ensureBounds();
const fromspot = link.computeSpot(true, fromport); // link's Spot takes precedence, if defined
const tonode = link.toNode;
tonode.ensureBounds();
const tospot = link.computeSpot(false, toport); // link's Spot takes precedence, if defined
const copy = list.copy();
const start = link.getLinkPoint(fromnode, fromport, fromspot, true, ortho, tonode, toport); // must be newly allocated result
const end = link.getLinkPoint(tonode, toport, tospot, false, ortho, fromnode, fromport); // must be newly allocated result
copy.insertAt(0, start);
copy.add(end);
return copy;
}
}
return list;
}
function savePoints(list, data, model) {
const copy = list.copy();
if (copy.length >= 2) {
copy.pop();
copy.removeAt(0);
}
return copy;
}
// initialize Palette
myPalette =
new go.Palette("myPaletteDiv", {
nodeTemplateMap: myDiagram.nodeTemplateMap,
model: new go.GraphLinksModel([
{ text: "red node", color: "red" },
{ text: "green node", color: "green" },
{ text: "blue node", color: "blue" },
{ text: "orange node", color: "orange" }
])
});
// initialize Overview
myOverview =
new go.Overview("myOverviewDiv", {
observed: myDiagram,
contentAlignment: go.Spot.Center
});
// save a model to and load a model from Json text, displayed below the Diagram
function save() {
const str = myDiagram.model.toJson();
document.getElementById("mySavedModel").value = str;
}
document.getElementById("mySaveButton").addEventListener("click", save);
function load() {
const str = document.getElementById("mySavedModel").value;
myDiagram.model = go.Model.fromJson(str);
}
document.getElementById("myLoadButton").addEventListener("click", load);
load();
</script>
</body>
</html>
Note that this does not only save points at which the route turns. It just discards the end points. On load it computes the expected end points.