I’m building a diagram editor and I’m having trouble with setting the initial data for both nodes and links.
When I set the initial data for the links, I get the following error:
go.js:18 Uncaught TypeError: Cannot read properties of undefined (reading 'x')
at Link.lN (go.js:18:116721)
at Link.makeGeometry (go.js:18:115293)
at Link.ad (go.js:18:112260)
at PanelLayoutLink.measure (go.js:13:234694)
at Link._u (go.js:13:259299)
at Link.ut (go.js:13:133033)
at Diagram.l0 (go.js:13:25853)
at Diagram.bS (go.js:13:78562)
at Diagram.yr (go.js:13:23626)
at Diagram.maybeUpdate (go.js:13:22818)
I’m loading my links from a JSON file using the default format for links in GoJS, which is:
{
"from": 1,
"to": 2,
"key": 1
}
When I try to remove the initial data for the links (by setting the linkDataArray to an empty array) and keep only the nodes, the nodes are loaded without text, as shown in the screenshot below:
When this happens, I cannot select any node by clicking on it directly, only by dragging a selection box around it.
If I add a new node, it is created with the text, and works normally, unless I try to connect it to one of the nodes that were loaded without text, which results in a crash (the tab memory usage goes up to 100% and the browser crashes).
Here’s the code (I’ve removed the JSON data for the nodes and links, but I can provide it if necessary, and I also removed the functions that are used to create the context menus and the buttons, as they are not relevant to the problem):
const nodeToolTip = go.GraphObject.build('ToolTip').add(
new go.TextBlock({ margin: sizes.xxs })
.bind('text')
);
const diagramContextMenu = go.GraphObject.build('ContextMenu').add(
makeContextMenuButton(
'Paste (Ctrl + V)',
(e) => e.diagram.commandHandler.pasteSelection(e.diagram.toolManager.contextMenuTool.mouseDownPoint),
(o) => o.diagram.commandHandler.canPasteSelection(o.diagram.toolManager.contextMenuTool.mouseDownPoint)
),
makeContextMenuButton(
'Undo (Ctrl + Z)',
(e) => e.diagram.commandHandler.undo(),
(o) => o.diagram.commandHandler.canUndo()
),
makeContextMenuButton(
'Redo (Ctrl + Y)',
(e) => e.diagram.commandHandler.redo(),
(o) => o.diagram.commandHandler.canRedo()
),
);
const nodeSelectionAdornmentTemplate = new go.Adornment('Spot')
.add(
new go.Panel('Auto').add(
new go.Shape({ fill: null, stroke: colors.blue, strokeWidth: 1.5, strokeDashArray: [4, 2] }),
new go.Placeholder({ margin: 1.5 })
)
)
.add(makeAppendNodeButton(new go.Spot(0.5, 0, 0, -sizes.xs), 'TriangleUp'))
.add(makeAppendNodeButton(new go.Spot(0, 0.5, -sizes.xs, 0), 'TriangleLeft'))
.add(makeAppendNodeButton(new go.Spot(1, 0.5, sizes.xs, 0), 'TriangleRight'))
.add(makeAppendNodeButton(new go.Spot(0.5, 1, 0, sizes.xs), 'TriangleDown'));
const nodeResizeAdornmentTemplate = new go.Adornment('Spot', {
locationSpot: go.Spot.Right
})
.add(new go.Placeholder());
const cursorSpotKeyMap = {
'nw-resize': go.Spot.TopLeft,
'n-resize': go.Spot.Top,
'ne-resize': go.Spot.TopRight,
'w-resize': go.Spot.Left,
'e-resize': go.Spot.Right,
'se-resize': go.Spot.BottomLeft,
's-resize': go.Spot.Bottom,
'sw-resize': go.Spot.BottomRight,
};
Object.entries(cursorSpotKeyMap).forEach(([cursor, spot]) => {
const resizeAdornmentShape = new go.Shape({
alignment: spot,
cursor: cursor,
desiredSize: new go.Size(sizes.xs, sizes.xs),
fill: colors.white,
stroke: colors.blue,
});
nodeResizeAdornmentTemplate.add(resizeAdornmentShape);
});
const diagramNodeTemplate = new go.Node('Auto', {
contextMenu: nodeContextMenu,
locationSpot: go.Spot.Center,
selectable: true,
selectionAdornmentTemplate: nodeSelectionAdornmentTemplate,
resizable: true,
resizeObjectName: 'resize-panel',
resizeAdornmentTemplate: nodeResizeAdornmentTemplate,
toolTip: nodeToolTip,
minSize: new go.Size(sizes.lg, sizes.lg),
})
.bindTwoWay('location', 'location', go.Point.parse, go.Point.stringify)
.add(
new go.Panel('Auto', { name: 'resize-panel' })
.bind('desiredSize', 'desiredSize', go.Size.parse, go.Size.stringify)
.add(
new go.Shape('RoundedRectangle', {
portId: '',
fromLinkable: true,
toLinkable: true,
stroke: 'transparent',
fill: colors.white,
strokeWidth: 0,
})
.bind('fill')
.bind('strokeWidth')
.bind('strokeDashArray', 'strokeDashArray', parseDashArray, stringifyDashArray)
.bind('figure'),
new go.TextBlock({
margin: sizes.xs,
wrap: go.Wrap.Fit,
editable: true,
})
.bindTwoWay('text')
.bind('stroke', 'stroke', getTextColor),
)
);
const makeAdornmentPathPattern = (linkStrokeWidth) => {
const shapeStrokeWidth = 2;
return new go.Shape({
stroke: colors.blue,
strokeWidth: shapeStrokeWidth,
strokeCap: 'square',
geometryString: `M0 0 M4 2 H3 M4 ${linkStrokeWidth * 2 + shapeStrokeWidth} H3`,
});
}
const linkSelectionAdornmentTemplate = new go.Adornment().add(
new go.Shape({
isPanelMain: true,
stroke: null,
strokeWidth: 6,
pathPattern: makeAdornmentPathPattern(2) // strokeWidth
})
.bind('pathPattern', 'strokeWidth', makeAdornmentPathPattern),
);
const diagramLinkTemplate = new go.Link({
contextMenu: linkContextMenu,
selectable: true,
selectionAdornmentTemplate: linkSelectionAdornmentTemplate,
relinkableFrom: true,
relinkableTo: true,
reshapable: true,
routing: go.Routing.AvoidsNodes,
curve: go.Curve.JumpOver,
corner: sizes.xxs,
})
.bind('points')
.add(
new go.Shape({
isPanelMain: true,
strokeWidth: 2,
stroke: colors.black,
})
.bind('stroke')
.bind('strokeWidth')
.bind('strokeDashArray', 'strokeDashArray', parseDashArray, stringifyDashArray),
new go.Shape({
fromArrow: '',
stroke: colors.black,
strokeWidth: 2,
fill: colors.black,
})
.bind('fromArrow'),
new go.Shape({
toArrow: 'Standard',
stroke: colors.black,
strokeWidth: 2,
fill: colors.black,
})
.bind('toArrow'),
);
const diagramModel = new go.GraphLinksModel(
nodeData,
linkData,
{
modelData: {
user: USER,
diagramId: DIAGRAM_ID,
},
linkKeyProperty: 'key',
makeUniqueKeyFunction: (model) => {
let key = 1;
while (model.findNodeDataForKey(key)) {
key++;
}
return key;
},
makeUniqueLinkKeyFunction: (model) => {
let key = 1;
while (model.findLinkDataForKey(key)) {
key++;
}
return key;
},
},
);
const diagram = new go.Diagram('diagram', {
initialPosition: new go.Point(0, 0),
contextMenu: diagramContextMenu,
nodeTemplate: diagramNodeTemplate,
linkTemplate: diagramLinkTemplate,
model: diagramModel,
'draggingTool.dragsLink': true,
'linkingTool.isUnconnectedLinkValid': false,
'linkingTool.portGravity': 20,
'relinkingTool.isUnconnectedLinkValid': false,
'relinkingTool.portGravity': 20,
'relinkingTool.fromHandleArchetype': new go.Shape('Rectangle', {
segmentIndex: 0,
desiredSize: new go.Size(sizes.xs, sizes.xs),
fill: colors.white,
stroke: colors.blue,
}),
'relinkingTool.toHandleArchetype': new go.Shape('Rectangle', {
segmentIndex: -1,
desiredSize: new go.Size(sizes.xs, sizes.xs),
fill: colors.white,
stroke: colors.blue,
}),
'linkReshapingTool.handleArchetype': new go.Shape('Rectangle', {
desiredSize: new go.Size(sizes.xs, sizes.xs),
fill: colors.white,
stroke: colors.blue,
}),
'undoManager.isEnabled': true,
});
I’m using the latest version of GoJS (v3.0.20).
Any help is greatly appreciated, please note that I’m new to GoJS and I’m still learning how to use it, and I couldn’t find any similar problems in the documentation or in the forums, so I’m not sure what I’m doing wrong.
Also, I couldn’t find any workaround for this problem, so if you have any suggestions, I would be very grateful.
Thank you in advance!