I’m not sure if I have discovered a bug or I’m doing something wrong but if try to update link routing after a model is changed and fully rendered, the links are not updated unless I move a node a tiny bit. See the following screencast:
if model A
is rendered, switching the link routing works as expected ✅
if model B
is rendered, switching the link routing works as expected ✅
if model A
is rendered, link routing is changed and then model B
is rendered the links stay the same until I slightly move a node ❌
Here’s part of the code I’m using:
const modelAButton = document.querySelector('#model_a');
const modelBButton = document.querySelector('#model_b');
const normalButton = document.querySelector('#normal');
const avoidNodesButton = document.querySelector('#avoid_nodes');
let routing = go.Routing.Normal;
var d = go.GraphObject.make(go.Diagram, 'box');
d.nodeTemplate = getNodeTemplate();
d.linkTemplate = getLinkTemplate();
const modelA = getModelA(); // just a GraphLinksModel
const modelB = getModelB(); // anther GraphLinksModel
d.model = modelA;
d.addDiagramListener('InitialLayoutCompleted', (e) => {
console.log(`model ${e.diagram.model.modelData.modelId} rendered`);
updateLinkRouting(e.diagram, routing);
});
modelAButton?.addEventListener('click', () => {
d.model = modelA;
});
modelBButton?.addEventListener('click', () => {
d.model = modelB;
});
normalButton?.addEventListener('click', () => {
routing = go.Routing.Normal;
updateLinkRouting(d, routing);
});
avoidNodesButton?.addEventListener('click', () => {
routing = go.Routing.AvoidsNodes;
updateLinkRouting(d, routing);
});
And here’s the code for updateLinkRouting
:
function updateLinkRouting(d: go.Diagram, routing: go.Routing) {
d.startTransaction('link_routing');
d.linkTemplate.routing = routing;
const endSegmentLength = routing === go.Routing.Normal ? 0 : NaN;
d.linkTemplate.fromEndSegmentLength = endSegmentLength;
d.linkTemplate.toEndSegmentLength = endSegmentLength;
d.links.each((l) => {
l.routing = routing;
l.fromEndSegmentLength = endSegmentLength;
l.toEndSegmentLength = endSegmentLength;
});
d.commitTransaction('link_routing');
}
walter
June 13, 2025, 4:00pm
2
I’ve re-created your situation as best as I can, but I am unable to reproduce any problem. Here’s my code. What am I missing?
<!DOCTYPE html>
<html lang="en">
<body>
<div id="sample">
<div id="myDiagramDiv" style="width: 100%; height: 400px; background-color: #dfebe5; border: solid 1px black"></div>
<div id="buttons">
<button onclick="load('A')">Model A</button>
<button onclick="load('B')">Model B</button>
<button onclick="updateLinkRouting('n')">Normal</button>
<button onclick="updateLinkRouting('a')">Avoids Nodes</button>
Diagram Model saved in JSON format:
</div>
<textarea id="mySavedModel" style="width:100%;height:300px"></textarea>
<script src="https://cdn.jsdelivr.net/npm/gojs/release/go-debug.js"></script>
<script id="code">
myDiagram = new go.Diagram('myDiagramDiv', {
layout: new go.ForceDirectedLayout(),
'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 =
new go.Node('Auto', {
})
.add(
new go.Shape('RoundedRectangle', { fill: "goldenrod" }),
new go.TextBlock({ margin: 8, editable: true })
.bindTwoWay('text')
);
const modelA = new go.GraphLinksModel(
[
{ key: 1, text: "A" },
{ key: 2, text: "B" },
{ key: 3, text: "C" }
],
[
{ from: 1, to: 2 },
{ from: 2, to: 3 }
]);
const modelB = new go.GraphLinksModel(
[
{ key: 1, text: "BB" },
{ key: 2, text: "BB1" },
{ key: 3, text: "BB2" }
],
[
{ from: 1, to: 2 },
{ from: 2, to: 3 },
{ from: 3, to: 1 }
]);
function load(modelname) {
myDiagram.model = modelname === 'A' ? modelA : modelB;
}
function updateLinkRouting(routing) {
const r = routing === 'a' ? go.Routing.AvoidsNodes : go.Routing.Normal;
const d = myDiagram;
d.startTransaction('link_routing');
d.linkTemplate.routing = r;
const endSegmentLength = r === go.Routing.Normal ? 0 : NaN;
d.linkTemplate.fromEndSegmentLength = endSegmentLength;
d.linkTemplate.toEndSegmentLength = endSegmentLength;
d.links.each((l) => {
l.routing = r;
l.fromEndSegmentLength = endSegmentLength;
l.toEndSegmentLength = endSegmentLength;
});
d.commitTransaction('link_routing');
}
</script>
</body>
</html>
Thanks.
Looks like a bug in older version. That ^ example was using 3.0.8
version and with the latest 3.0.23
I can’t repro the issue. Let me update our codebase and I’ll report back.
It wasn’t the version.
I think I figured this out. This wasn’t about the version at all. The only difference between my code and yours was that I had a two way binding on the points
for links:
function getLinkTemplate() {
return new go.Link()
.add(new go.Shape({ strokeWidth: 2, stroke: 'black' }))
.add(new go.Shape({ toArrow: 'Standard', alignment: go.Spot.Center }))
.bind(new go.Binding('points').makeTwoWay());
}
This wasn’t in my original post for you to see and I have no idea why I have that binding cause we don’t update the links points programmatically and externally outside of the diagram. So this was unnecessary. Removing it fixes the issue.
walter
June 13, 2025, 9:28pm
5
If you do want to support a TwoWay Binding on the Link.points property, you’ll need to be careful to remove the old routes when loading the model, unless the desired routing is the same as what the model had when it was saved.