Links not connecting with collapsed Group

Hi. I’ve got a demo of the problem for you.

If you group some nodes you’ll see that the final outgoing link doesn’t get redrawn. I think the incoming link doesn’t either, it’s just harder to see.

If you change the SelectionGrouped event handler so that it sets the group location instead of calling the move function then the links get re-drawn, but then expanding the nodes causes the group to move (see other post).

Hopefully this is a helpful demo?

Thanks!

<!DOCTYPE html>
<html>
<head>
    <title>Minimal GoJS Sample</title>
    <!-- Copyright 1998-2022 by Northwoods Software Corporation. -->
</head>
<body>
    <div id="diagram" style="border: solid 1px black; width:100%; height:600px"></div>
    <script src="https://unpkg.com/[email protected]"></script>
    <script id="code">
        const $ = go.GraphObject.make;

        const KAPPA = 4 * ((Math.sqrt(2) - 1) / 3);
        go.Shape.defineFigureGenerator("HalfEllipse", function (shape, w, h) {
            return new go.Geometry()
                .add(new go.PathFigure(0, 0, true)
                    .add(new go.PathSegment(go.PathSegment.Bezier, w, .5 * h, KAPPA * w, 0, w, (.5 - KAPPA / 2) * h))
                    .add(new go.PathSegment(go.PathSegment.Bezier, 0, h, w, (.5 + KAPPA / 2) * h, KAPPA * w, h).close()))
                .setSpots(0, 0.156, 0.844, 0.844);
        });
        go.Shape.defineFigureGenerator("RoundedTopRectangle", function (shape, w, h) {
            // this figure takes one parameter, the size of the corner
            let p1 = 5;  // default corner size
            if (shape !== null) {
                let param1 = shape.parameter1;
                if (!isNaN(param1) && param1 >= 0) p1 = param1;  // can't be negative or NaN
            }
            p1 = Math.min(p1, w / 2);
            p1 = Math.min(p1, h / 2);  // limit by whole height or by half height?
            let geo = new go.Geometry();
            // a single figure consisting of straight lines and quarter-circle arcs
            geo.add(new go.PathFigure(0, p1)
                .add(new go.PathSegment(go.PathSegment.Arc, 180, 90, p1, p1, p1, p1))
                .add(new go.PathSegment(go.PathSegment.Line, w - p1, 0))
                .add(new go.PathSegment(go.PathSegment.Arc, 270, 90, w - p1, p1, p1, p1))
                .add(new go.PathSegment(go.PathSegment.Line, w, h))
                .add(new go.PathSegment(go.PathSegment.Line, 0, h).close()));
            // don't intersect with two top corners when used in an "Auto" Panel
            geo.spot1 = new go.Spot(0, 0, 0.3 * p1, 0.3 * p1);
            geo.spot2 = new go.Spot(1, 1, -0.3 * p1, 0);
            return geo;
        });

        const diagram = $(
            go.Diagram,
            "diagram",
            {
                layout: $(go.LayeredDigraphLayout, { isInitial: false, isOngoing: false }),
                "commandHandler.archetypeGroupData": { isGroup: true },
                "SelectionGrouped": e => {
                    e.subject.location = e.subject.memberParts.first().location;
                    // e.subject.move(e.subject.memberParts.first().location, true);
                },
                "undoManager.isEnabled": true,
                "draggingTool.isGridSnapEnabled": true,
            }
        );

        diagram.grid =
            $(go.Panel, "Grid",
                { visible: true, gridCellSize: new go.Size(30, 30) },
                $(go.Shape, "LineH", { stroke: "#E8E8E8" }),
                $(go.Shape, "LineV", { stroke: "#E8E8E8" }),
            );

        const inPortTemplate = $(
            go.Panel,
            "TableRow",
            {},
            $(
                go.Shape,
                "HalfEllipse",
                {
                    angle: 180,
                    cursor: "pointer",
                    fromLinkable: false,
                    fromMaxLinks: 1,
                    toSpot: go.Spot.Right,
                    margin: new go.Margin(5, -1, 5, 0),
                    toLinkable: true,
                    toMaxLinks: 1,
                    fill: "black",
                    desiredSize: new go.Size(9, 18),
                },
                new go.Binding("portId", ""),
            )
        );

        const outPortTemplate = $(
            go.Panel,
            "TableRow",
            {},
            $(
                go.Shape,
                "HalfEllipse",
                {
                    cursor: "pointer",
                    fromLinkable: true,
                    fromMaxLinks: 1,
                    fromSpot: go.Spot.Right,
                    margin: new go.Margin(5, 0, 5, -1),
                    toLinkable: false,
                    toMaxLinks: 1,
                    fill: "black",
                    desiredSize: new go.Size(9, 18),
                },
                new go.Binding("portId", ""),
            )
        );

        diagram.nodeTemplate = $(
            go.Node,
            "Horizontal",
            new go.Binding("location", "location", go.Point.parse).makeTwoWay(go.Point.stringify),
            { locationSpot: go.Spot.Center },
            $(
                go.Panel,
                "Table",
                {
                    itemTemplate: inPortTemplate,
                },
                new go.Binding("itemArray", "inPortIds")
            ),
            $(
                go.Panel,
                "Auto",
                {
                    desiredSize: new go.Size(80, 80),
                },
                $(
                    go.Shape,
                    { fill: "white" },
                    new go.Binding("fill", "color")
                ),
                $(go.TextBlock, new go.Binding("text")),
            ),
            $(
                go.Panel,
                "Table",
                {
                    itemTemplate: outPortTemplate,
                },
                new go.Binding("itemArray", "outPortIds")
            ),
        );

        diagram.groupTemplate = $(
            go.Group,
            "Table",
            {
                locationSpot: go.Spot.Center,
                ungroupable: true,
                isSubGraphExpanded: false,
                toSpot: go.Spot.Left,
                fromSpot: go.Spot.Right,
            },
            new go.Binding("location", "location", go.Point.parse).makeTwoWay(go.Point.stringify),
            { doubleClick: (e, grp) => {
                    if (grp.isSubGraphExpanded) {
                        e.diagram.commandHandler.collapseSubGraph();
                    } else {
                        e.diagram.commandHandler.expandSubGraph();
                    }
                }
            },
            // If you remove this heading row then you can get the group to align
            $(
                go.Panel,
                "Auto",
                {
                    stretch: go.GraphObject.Horizontal,
                    row: 0,
                    column: 0,
                },
                $(
                    go.Shape,
                    "RoundedTopRectangle",
                    {
                        strokeWidth: 2,
                        stroke: "black",
                        margin: new go.Margin(0, 0, -1.5, 0),
                        fill: "white",
                    },
                ),
                $(
                    go.Panel,
                    "Vertical",
                    {
                        padding: 12,
                        stretch: go.GraphObject.Fill
                    },
                    $(
                        go.TextBlock,
                        "Default Text",
                        {
                            isMultiline: false,
                            alignment: go.Spot.Center,
                            wrap: go.TextBlock.None,
                            overflow: go.TextBlock.OverflowEllipsis,
                            maxSize: new go.Size(175, Infinity),
                            text: "Group Title"
                        },
                    ),
                ),
            ),
            $(
                go.Panel,
                "Auto",
                {
                    stretch: go.GraphObject.Horizontal,
                    row: 1,
                    column: 0,
                },
                $(
                    go.Shape,
                    "Square",
                    {
                        strokeWidth: 2,
                        stroke: "black",
                        fill: "white"
                    },
                ),
                $(
                    go.Panel,
                    "Vertical",
                    {
                        stretch: go.GraphObject.Horizontal,
                        padding: new go.Margin(8, 0, 5, 0),
                    },
                    new go.Binding("visible", "isSubGraphExpanded", (isExpanded) => !isExpanded).ofObject(),
                    $(
                        go.Panel,
                        "Vertical",
                        {
                            name: "icon",
                            margin: new go.Margin(0, 10, 0, 10),
                        },
                        $(go.TextBlock, {text: "This is a group"}),
                        $(go.TextBlock, {text: "On two lines"}),
                    ),
                ),
                $(go.Placeholder, { padding: 20 })
            )
        );

        diagram.linkTemplate = $(
            go.Link,
            {
                selectionAdorned: false,
                routing: go.Link.Normal,
                relinkableTo: true,
                fromEndSegmentLength: 10,
                toEndSegmentLength: 10,
            },
            $(
                go.Shape,
                {
                    strokeWidth: 3,
                },
            )
        );

        diagram.model = new go.GraphLinksModel(
            [
                { key: 1, text: "Alpha", color: "lightblue", location: "-300 0", inPortIds: [], outPortIds: ["out"]},
                { key: 2, text: "Beta", color: "orange", location: "-150 0", inPortIds: ["in"], outPortIds: ["out"]},
                { key: 3, text: "Gamma", color: "lightgreen", location: "0 0", inPortIds: ["in"], outPortIds: ["out"]},
                { key: 4, text: "Delta", color: "pink", location: "150 0", inPortIds: ["in"], outPortIds: [] },

                // If you remove these nodes then expanding a group doesn't cause such a large movement
                { key: 5, text: "Alpha", color: "lightblue", location: "-300 500", inPortIds: [], outPortIds: ["out"]},
                { key: 6, text: "Beta", color: "orange", location: "-150 500", inPortIds: ["in"], outPortIds: ["out"]},
                { key: 7, text: "Gamma", color: "lightgreen", location: "0 500", inPortIds: ["in"], outPortIds: ["out"]},
                { key: 8, text: "Delta", color: "pink", location: "150 500", inPortIds: ["in"], outPortIds: [] }
            ],
            [
                { from: 1, to: 2, fromPort: "out", toPort: "in" },
                { from: 2, to: 3, fromPort: "out", toPort: "in" },
                { from: 3, to: 4, fromPort: "out", toPort: "in" },

                { from: 5, to: 6, fromPort: "out", toPort: "in" },
                { from: 6, to: 7, fromPort: "out", toPort: "in" },
                { from: 7, to: 8, fromPort: "out", toPort: "in" },
            ]
        );
        diagram.model.linkFromPortIdProperty = "fromPort";
        diagram.model.linkToPortIdProperty = "toPort";
    </script>
</body>
</html>