Draw unconnected links

Hi,

I am getting an error when I try to create an unconnected link

The diagram configuration is

private createDiagram(): go.Diagram {
        return $(go.Diagram, 'diagram-div', {
            allowDrop: true,
            allowLink: true,
            initialContentAlignment: go.Spot.Center,
            'animationManager.isEnabled': false,  // turn off automatic animations
            'draggingTool.isGridSnapEnabled': false,
            'undoManager.isEnabled': true,
            'grid.visible': false,
            'grid.gridCellSize': new go.Size(20, 20),
            'linkingTool.isUnconnectedLinkValid': true,
            'linkingTool.portGravity': 20,
            'relinkingTool.isUnconnectedLinkValid': true,
            'relinkingTool.portGravity': 20,
            'draggingTool.dragsLink': true
        });
    }

The model configuration is

private configureModel(): void {
    this.diagram.model = $(go.GraphLinksModel,
        {
            linkFromPortIdProperty: 'fromPortName',
            linkToPortIdProperty: 'toPortName',
            nodeKeyProperty: 'uuid',
            linkKeyProperty: 'uuid'
        }
    );
}

The node template is

templateMap.add('geometryWithLabel',
            $(go.Node, this.nodeStyle(),
                $(go.Panel, this.panelShapePortStyle(),
                    $(go.Shape, this.shapeGeometryStyle()),
                    new go.Binding('itemArray', 'ports'),
                    {
                        itemTemplate: $(go.Panel, this.portPanelStyle(),
                            $(go.Shape, this.portStyle())
                        )
                    },
                ),
                $(go.TextBlock, this.textBlockStyle()),
            ),
        );

private nodeStyle() {
        return [
            {
                type: go.Panel.Spot,
                selectionAdorned: false,
                shadowOffset: new go.Point(0, 0),
                shadowBlur: 15,
                shadowColor: 'blue',
                resizable: true,
                resizeObjectName: 'NODESHAPE',
                locationObjectName: 'NODESHAPE',
                selectionObjectName: 'NODESHAPE',
                locationSpot: go.Spot.Center,
                cursor: 'move',
            },
            new go.Binding('location', 'location', go.Point.parse).makeTwoWay(go.Point.stringify),
            new go.Binding('isShadowed', 'isSelected').ofObject()
        ];
    }

    private panelShapePortStyle() {
        return [
            {
                type: go.Panel.Spot,
                name: 'NODEPANEL'
            },
        ];
    }

    private shapeGeometryStyle() {
        return [
            {
                name: 'NODESHAPE',
                fill: 'white',
                stroke: 'black',
                strokeWidth: 0.80,
                strokeCap: 'round',
                strokeJoin: 'round',
                strokeDashArray: null,
                geometryStretch: go.GraphObject.Uniform
            },
            new go.Binding('geometryString', 'figure'),
            new go.Binding('width', 'width').makeTwoWay(),
            new go.Binding('height', 'height').makeTwoWay(),
            new go.Binding('stroke', 'lineColor'),
            new go.Binding('strokeWidth', 'lineWidth')
        ];
    }

    private portPanelStyle() {
        return [
            {
                type: go.Panel.Spot
            },
            new go.Binding('portId', 'name'),
            new go.Binding('fromSpot', 'fromSpot', go.Spot.parse),
            new go.Binding('toSpot', 'toSpot', go.Spot.parse),
            new go.Binding('alignment', 'alignment', go.Spot.parse),
            new go.Binding('fromLinkable', '', ((port: Port) => {
                switch (port.type) {
                    case PortType.Input:
                        return false;
                    case PortType.Output:
                    case PortType.InputOutput:
                        return true;
                }
            })),
            new go.Binding('fromMaxLinks', '', ((port: Port) => {
                switch (port.type) {
                    case PortType.Input:
                        return 0;
                    case PortType.Output:
                    case PortType.InputOutput:
                        return 1;
                }
            })),
            new go.Binding('toLinkable', '', ((port: Port) => {
                switch (port.type) {
                    case PortType.Output:
                        return false;
                    case PortType.Input:
                    case PortType.InputOutput:
                        return true;
                }
            })),
            new go.Binding('toMaxLinks', '', ((port: Port) => {
                switch (port.type) {
                    case PortType.Output:
                        return 0;
                    case PortType.Input:
                    case PortType.InputOutput:
                        return 1;
                }
            }))
        ];
    }

    private portStyle() {
        return [
            {
                name: 'PORT',
                figure: 'XLine',
                width: 10,
                height: 10,
                background: 'transparent',
                fill: null,
                stroke: null,
                angle: 90,
                cursor: 'pointer'
            }
        ];
    }

The link template is

private createLinkTemplate(): go.Link {
    return $(go.Link,
        {
            routing: go.Link.AvoidsNodes,
            curve: go.Link.JumpOver,
            corner: 3,
            reshapable: true,
            resegmentable: true,
            relinkableFrom: true,
            relinkableTo: true,
            selectionAdorned: false,
            shadowOffset: new go.Point(0, 0),
            shadowBlur: 15,
            shadowColor: 'blue',
            toShortLength: -5,
            fromShortLength: -5,
        },
        new go.Binding('points').makeTwoWay(),
        new go.Binding('isShadowed', 'isSelected').ofObject(),
        $(go.Shape,
            {
                strokeWidth: 1,
                stroke: 'black',
                strokeDashArray: null,
            },
            new go.Binding('stroke', 'lineColor'),
            new go.Binding('strokeWidth', 'lineWidth')            ),
        $(go.Shape,
            {
                toArrow: 'Triangle',
                segmentOffset: new go.Point(5, 0),
                stroke: 'black',
                fill: 'black'
            },
            new go.Binding('fill', 'lineColor'),
            new go.Binding('stroke', 'lineColor')
        )
    );
}

I am initializing the model with a node.

{
                name: "DP",
                shapeName: "DP",
                location: "-413.788837962963 -80.1428714739526",
                height: 57.714255904705304,
                width: 56,
                labelOffset: "0.5 0.5 0 0",
                category: "geometryWithoutLabel",
                figure: "F M15.72 587.18 A7.85941 8.1 -180 1 0 0 587.18 A7.85941 8.1 -180 1 0 15.72 587.18 Z M8.02 586.85 L10.67 582.93 L8.66 587.18 L8.02 586.85 ZM8.82 587.98 a0.962376 0.962376 -180 1 0 -1.92475 0 a0.962376 0.962376 -180 1 0 1.92475 0 ZM2.49 590.54 C1.92 589.58 1.52 588.38 1.52 587.18 C1.52 583.57 4.33 580.68 7.86 580.68 C11.31 580.68 14.2 583.57 14.2 587.18 C14.2 588.46 13.87 589.58 13.23 590.54M15.72 587.18 A7.85941 8.1 -180 1 0 0 587.18 A7.85941 8.1 -180 1 0 15.72 587.18M8.02 586.85 L10.67 582.93 L8.66 587.18M8.82 587.98 a0.962376 0.962376 -180 1 0 -1.92475 0 a0.962376 0.962376 -180 1 0 1.92475 0",
                lineWidth: 1,
                lineColor: "#000000",
                fillColor: "#ffffff",
                linePattern: 0,
                brushType: 0,
                gradientColor: "#000000",
                ports: [{
                    name: "port1",
                    type: 0,
                    fromSpot: "",
                    toSpot: "LeftSide",
                    alignment: "0 0.5 0 0"
                }, {name: "port2", type: 1, fromSpot: "RightSide", toSpot: "", alignment: "1 0.5 0 0"}],
                uuid: "{7459b734-9dc9-487d-897e-92918b013bc7}",
                blockType: 34,
                text: "",
                fontStyle: 0,
                fontWeight: 1,
                fontSize: 12,
                fontFamily: 'helvetica, sans-serif',
                fontUnderline: false,
                textColor: '#000000',
                textBackgroundColor: null,
                textAlign: TextAlign.Center,
                textEditable: true,
                getPort: VMNode.prototype.getPort
            }

When i try to create a disconnected link from the left port of the shape (input port), i am getting the error

But when i create a link from the right port of the shape (output port), no error is reported.

I created an extension of go.Link to report the value of the getLinkPoint parameters. I did the same extension in the example “Draggable Link” to compare and the parameters seem be equivalent.

Thanks,
Dario

I don’t know what might be wrong. I just tried the unmodified Draggable Link sample, and I was able to draw a new link starting at an input port, resulting in a link with no “from” connection and with the “to” end connected to that input port. (I used an “End” node.)

Maybe there’s something wrong with your override of Link.getLinkPoint. Although it’s suspicious that your link template doesn’t use the TestLink class.

Hi Walter

The override is ok. The error stack was of a previous run when i had extended the class go.Link with TestLink.
I can’t understand why the problem is only with input ports.
Which is the object whose property pi needs to be read?
Thanks

I can’t tell. Has the go.js file been webpacked? Precisely which version of go.js are you using?

I am using the 1.7.16 version.

What happens if you use version 1.7.29, the last of the 1.7.* branch? Or latest (1.8.*)?

I will try with that version and i will let you know
Thanks

Hi Walter,
In the port template i am binding the values of fromSpot and toSpot with model values

new go.Binding('fromSpot', 'fromSpot', go.Spot.parse),
new go.Binding('toSpot', 'toSpot', go.Spot.parse),

For an input port
fromSpot --> ‘’
toSpot --> ‘LeftSide’

If i replace the toSpot binding with a specific value

toSpot: go.Spot.Left

the error disappears.

Am i doing anything wrong with the binding?

Thanks.

You can call Spot.parse on various strings to see what they return, or if it throws an exception. I would not think that an empty string would be a valid Spot string.

Hi Walter, i found the problem
In the model I was using the values ‘TopSide’, ‘BottomSide’, ‘LeftSide’ and ‘RightSide’ to represent the spot where each node have to be localized.
When i replaced these values by ‘Top’, ‘Bottom’, ‘Left’ and ‘Right’ , the error was fixed.

Thanks very much for your support.

Regards,
Dario