Hi Walter, I found another link routing issue. In the following diagram
I want to add a box between Box9 and Box15 (on the right-hand side of Box9).
What I got is following
Notice that Box15 is at a wrong location. Or I can say the link from Box7 to Box13 is routed incorrectly because the link is overlapped with Box 15. Same routing issue happens with the link from Box10 to Box13 and the link from Box11 to Box13.
It is another routing issue only with a node being dynamically added. When the diagram is loaded with the updated nodes and links, the diagram layout and link routings are correct. However, the problematic links are not associated with the end node this time :(
I am pasting the HTML + JS code below. Please only click the Add Box once, which will add Box16 on the right-hand side of Box9. Thank you so much!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div
id="myDiagramDiv"
style="border: solid 1px black; width: 100%; height: 900px"
></div>
<div class="row">
<button id="add">Add Box</button>
</div>
<script src="../../site/release/go-debug.js"></script>
<script>
let myDiagram;
function init() {
const $ = go.GraphObject.make;
myDiagram = $(go.Diagram, "myDiagramDiv", {
layout: $(go.LayeredDigraphLayout, {
layeringOption: go.LayeredDigraphLayout.LayerLongestPathSource,
packOption: go.LayeredDigraphLayout.PackStraighten,
aggressiveOption: go.LayeredDigraphLayout.AggressiveNone,
layerSpacing: 50,
linkSpacing: 20,
alignOption: go.LayeredDigraphLayout.AlignAll,
}),
});
myDiagram.nodeTemplateMap.add("global", $(
go.Node,
"Spot",
$(go.Shape, "Circle", {
fill: "transparent",
stroke: '#01778e',
width: 50,
height: 50,
portId: "",
fromLinkable: true,
toLinkable: true,
cursor: "pointer",
}),
$(
go.TextBlock,
new go.Binding("text", "key"),
{
verticalAlignment: go.Spot.Center,
textAlign: "center",
stroke: '#1d2024',
},
),
$(go.Shape, "Rectangle", {
fill: null,
stroke: null,
desiredSize: new go.Size(0, 0),
alignment: new go.Spot(0, 0.5, 0, 0),
portId: "Left",
fromSpot: go.Spot.Left,
toSpot: go.Spot.Left,
}),
$(go.Shape, "Rectangle", {
fill: null,
stroke: null,
desiredSize: new go.Size(0, 0),
alignment: new go.Spot(1, 0.5, 0, 0),
portId: "Right",
fromSpot: go.Spot.Right,
toSpot: go.Spot.Right,
}),
));
myDiagram.nodeTemplateMap.add("", $(
go.Node,
"Spot",
$(go.Shape, "Rectangle", {
fill: "transparent",
stroke: "transparent",
width: 124,
height: 152,
portId: "",
fromLinkable: true,
toLinkable: true,
cursor: "pointer",
}),
$(
go.Panel,
"Position",
$(go.Shape, "RoundedRectangle", {
fill: "transparent",
width: 64,
height: 64,
position: new go.Point(0, 12),
}),
$(
go.TextBlock,
new go.Binding("text", "key"),
{
width: 64,
position: new go.Point(0, 84),
verticalAlignment: go.Spot.Center,
textAlign: "center",
},
),
),
$(go.Shape, "Rectangle", {
fill: null,
stroke: null,
desiredSize: new go.Size(0, 0),
alignment: new go.Spot(0, 0.5, 30, 0),
portId: "Left",
fromSpot: go.Spot.Left,
toSpot: go.Spot.Left,
}),
$(go.Shape, "Rectangle", {
fill: null,
stroke: null,
desiredSize: new go.Size(0, 0),
alignment: new go.Spot(1, 0.5, -30, 0),
portId: "Right",
fromSpot: go.Spot.Right,
toSpot: go.Spot.Right,
}),
));
myDiagram.nodeTemplateMap.add("group-end", $(go.Node, 'Spot',
$(go.Shape, "Circle", {
width: 0,
height: 0,
alignment: go.Spot.Center,
fill: null,
stroke: null,
}),
$(go.Shape, "Rectangle", {
fill: null,
stroke: null,
desiredSize: new go.Size(0, 0),
alignment: new go.Spot(0, 0.5),
portId: "Left",
fromSpot: go.Spot.Left,
toSpot: go.Spot.Left,
}),
$(go.Shape, "Rectangle", {
fill: null,
stroke: null,
desiredSize: new go.Size(0, 0),
alignment: new go.Spot(1, 0.5),
portId: "Right",
fromSpot: go.Spot.Right,
toSpot: go.Spot.Right,
}),
));
myDiagram.linkTemplate = $(
go.Link,
go.Link.Orthogonal,
{
relinkableFrom: true,
relinkableTo: true,
selectable: true,
reshapable: true,
corner: 10,
toShortLength: 8,
fromEndSegmentLength: 40,
toEndSegmentLength: 40,
routing: go.Link.AvoidsNodes,
},
$(go.Shape, { isPanelMain: true, strokeWidth: 1 }),
$(go.Shape, {
toArrow: "Standard",
}),
);
myDiagram.groupTemplate = $(
go.Group,
"Spot",
{
layout: $(go.LayeredDigraphLayout, {
layeringOption: go.LayeredDigraphLayout.LayerLongestPathSource,
packOption: go.LayeredDigraphLayout.PackStraighten,
aggressiveOption: go.LayeredDigraphLayout.AggressiveNone,
layerSpacing: 50,
linkSpacing: 20,
alignOption: go.LayeredDigraphLayout.AlignAll,
isRouting: false
}),
},
$(go.Panel, "Horizontal",
$(go.Shape, "Rectangle", {
width: 30,
fill: "transparent",
strokeWidth: 0,
stretch: go.GraphObject.Fill
}),
$(go.Panel, "Auto",
$(go.Shape, "Rectangle", {
fill: "transparent",
strokeWidth: 0,
stretch: go.GraphObject.Fill
}),
$(go.Panel, "Auto",
$(
go.Shape,
"RoundedRectangle", // surrounds everything
{
parameter1: 10,
fill: "white",
stroke: '#999999',
minSize: new go.Size(240, 220),
spot1: go.Spot.TopLeft,
spot2:go.Spot.BottomRight
}
),
$(
go.Panel,
"Vertical", // position header above the subgraph
$(go.Panel, "Auto", {
height: 28,
alignment: go.Spot.Left,
padding: new go.Margin(8, 12, 0, 12),
}, $(go.TextBlock, // stage title near top, next to button
new go.Binding("text", "key")
)),
$(
go.Placeholder, // represents area for all member parts
{
padding: new go.Margin(4, 0, 4, 0),
background: "transparent",
minSize: new go.Size(240, 160),
}
),
$(go.Panel, "Auto", {
height: 28,
stretch: go.GraphObject.Fill
})
)
)
),
$(go.Shape, "Rectangle", {
width: 30,
fill: "transparent",
strokeWidth: 0,
stretch: go.GraphObject.Fill
})
),
$(go.Shape, "Rectangle", {
fill: null,
stroke: null,
desiredSize: new go.Size(0, 0),
alignment: new go.Spot(0, 0.5, 30, 0),
portId: "Left",
fromSpot: go.Spot.Left,
toSpot: go.Spot.Left,
}),
$(go.Shape, "Rectangle", {
fill: null,
stroke: null,
desiredSize: new go.Size(0, 0),
alignment: new go.Spot(1, 0.5, -30, 0),
portId: "Right",
fromSpot: go.Spot.Right,
toSpot: go.Spot.Right,
}
));
const nodeDataArray = [
{
key: "Start",
category: "global",
},
{
key: "End",
category: "global",
},
{
key: "Group1",
isGroup: true,
},
{
key: "Group1-start",
group: "Group1",
category: "group-end"
},
{
key: "Group1-end",
group: "Group1",
category: "group-end"
},
{
key: "Box1",
group: "Group1",
},
{
key: "Box2",
group: "Group1",
},
{
key: "Box3",
group: "Group1",
},
{
key: "Box4",
group: "Group1",
},
{
key: "Box5",
group: "Group1",
},
{
key: "Box6",
group: "Group1",
},
{
key: "Box7",
group: "Group1",
},
{
key: "Box8",
group: "Group1",
},
{
key: "Box9",
group: "Group1",
},
{
key: "Box10",
group: "Group1",
},
{
key: "Box11",
group: "Group1",
},
{
key: "Box12",
group: "Group1",
},
{
key: "Box13",
group: "Group1",
},
{
key: "Box14",
group: "Group1",
},
{
key: "Box15",
group: "Group1",
},
];
const linkDataArray = [
{
from: "Start",
to: "Group1",
fromPort: "Right",
toPort: "Left",
},
{
from: "Group1",
to: "End",
fromPort: "Right",
toPort: "Left",
},
{
from: "Group1-start",
to: "Box2",
fromPort: "Right",
toPort: "Left",
},
{
from: "Box2",
to: "Box1",
fromPort: "Right",
toPort: "Left",
},
{
from: "Box2",
to: "Box3",
fromPort: "Right",
toPort: "Left",
},
{
from: "Box2",
to: "Box6",
fromPort: "Right",
toPort: "Left",
},
{
from: "Box2",
to: "Box8",
fromPort: "Right",
toPort: "Left",
},
{
from: "Box1",
to: "Box4",
fromPort: "Right",
toPort: "Left",
},
{
from: "Box3",
to: "Box5",
fromPort: "Right",
toPort: "Left",
},
{
from: "Box3",
to: "Box7",
fromPort: "Right",
toPort: "Left",
},
{
from: "Box6",
to: "Box9",
fromPort: "Right",
toPort: "Left",
},
{
from: "Box6",
to: "Box10",
fromPort: "Right",
toPort: "Left",
},
{
from: "Box8",
to: "Box11",
fromPort: "Right",
toPort: "Left",
},
{
from: "Box5",
to: "Box4",
fromPort: "Right",
toPort: "Left",
},
{
from: "Box4",
to: "Box14",
fromPort: "Right",
toPort: "Left",
},
{
from: "Box14",
to: "Box13",
fromPort: "Right",
toPort: "Left",
},
{
from: "Box7",
to: "Box13",
fromPort: "Right",
toPort: "Left",
},
{
from: "Box9",
to: "Box15",
fromPort: "Right",
toPort: "Left",
},
{
from: "Box15",
to: "Box13",
fromPort: "Right",
toPort: "Left",
},
{
from: "Box10",
to: "Box13",
fromPort: "Right",
toPort: "Left",
},
{
from: "Box11",
to: "Box13",
fromPort: "Right",
toPort: "Left",
},
{
from: "Box13",
to: "Box12",
fromPort: "Right",
toPort: "Left",
},
{
from: "Box12",
to: "Group1-end",
fromPort: "Right",
toPort: "Left",
}
];
const model = new go.GraphLinksModel();
model.nodeDataArray = nodeDataArray;
model.linkDataArray = linkDataArray;
model.linkFromPortIdProperty = "fromPort";
model.linkToPortIdProperty = "toPort";
model.nodeGroupKey = "group";
myDiagram.model = model;
}
window.addEventListener("DOMContentLoaded", init);
document.getElementById('add').addEventListener('click', e => {
const node = myDiagram.findNodeForKey('Box9');
const groupKey = node.data.group;
const linkIt = node.findLinksOutOf();
const linksOutOf = new go.List();
while (linkIt.next()) {
linksOutOf.add(linkIt.value);
}
const existingKey = node.data.key;
const newKey = 'Box16';
myDiagram.model.addNodeData({ key: newKey, group: groupKey });
myDiagram.model.addLinkData({
from: existingKey,
fromPort: "Right",
to: newKey,
toPort: "Left",
});
linksOutOf.each(link => {
const toNodeKey = link.toNode.data.key;
myDiagram.model.removeLinkData(link.data);
myDiagram.model.addLinkData({
from: newKey,
fromPort: "Right",
to: toNodeKey,
toPort: "Left",
hidden: link.data.hidden,
});
});
});
</script>
</body>
</html>