Make links overlap nodes

We have a use-case where we will have invisible nodes (hidden with opacity: 0) in the diagram and we would like to be able to draw links over them (the hidden nodes) and not avoid them. Could you tell me how to accomplish this?

Do your links have Link.routing set to go.Link.AvoidsNodes?

If so, you can set or bind Node.avoidable to false on those that want to be ignored by link routing. Node | GoJS API

Hi Walter, I would like to continue this thread, which was started by one of my colleagues.

What we want to explore is a way to delete a node in a diagram without messing up its layout.

The diagram below is using LayeredDigraphLayout, and we want to delete Box6.

If we simply delete Box6, the layout changes dramatically, which is not what we want. Box3, Box4 and Box5 were moved to the first vertical layer as the START node because they do not have any incoming links.

Our use case is to allow users to temporarily delete Box6 so that they can add more links into Box3, Box4, and Box5. When users are modifying the diagram, It is critical to keep the current layout.

Our approach is to set the opacity of Box6 and its associated links to be 0. We marked Box6 as isHidden and did the following data binding, which worked well.

$(
      go.Node,
      new go.Binding("opacity", "", data => {
        return data.isHidden ? 0 : 1;
      }),
$(
    go.Link,
    new go.Binding("opacity", "", data => {
      const nodeFrom = myDiagram.findNodeForKey(data.from);
      const nodeTo = myDiagram.findNodeForKey(data.to);
      return nodeFrom.data.isHidden || nodeTo.data.isHidden ? 0 : 1;
    }),

But the issue is that when users add more links between the group of (Box1, Box2) and the group of (Box3, Box4, Box5), the newly added links are not routed “naturally” since they try to avoid the hidden Box6.


I changed the opacity of Box6 from 0 to 0.1 to illustrate the routing issue.

I have taken your suggestion to use Node.avoidable and go.Link.AvoidsNodes, as shown in the code snippet below. But the issue has not been solved.

$(go.Node, { avoidable: true },
$(go.Link, { routing: go.Link.AvoidsNodes, corner: 16, reshapable: true },

Do you have any suggestion on how to solve the routing issue? Thanks so much for your time!

It looks like you’ve set avoidable to true. It should be false if you want links to ignore it during AvoidsNodes routing. Maybe you want to bind it to your isHidden property.

new go.Binding("opacity", "isHidden", h => h ? 0 : 1),
new go.Binding("avoidable", "isHidden" h => !h)

Hi jhardy,

I tried your suggestion but it does not work. Note that I changed the opacity of Box6 to 0.1. And I have the following code snippet in my node template.

new go.Binding("avoidable", "isHidden", h => !h),
new go.Binding("opacity", "isHidden", h => {
  return h ? 0.1 : 1;
}),

Moreover, I do not use go.Link.AvoidsNodes anymore. Now I am using go.Link.Orthogonal.

I am posting the complete HTML + JS code below.

Thanks for your time!

<!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: 700px"
    ></div>
    <script src="../../release/go-debug.js"></script>
    <script>
      const nodeDataArray = [
        {
          key: "START",
        },
        {
          key: "Box1",
        },
        {
          key: "Box2",
        },
        {
          key: "Box3",
        },
        {
          key: "Box4",
        },
        {
          key: "Box5",
        },
        {
          key: "Box6",
          isHidden: true,
        },
        {
          key: "END",
        },
      ];

      const linkDataArray = [
        {
          from: "START",
          to: "Box1",
        },
        {
          from: "START",
          to: "Box2",
        },
        {
          from: "Box1",
          to: "Box6",
        },
        {
          from: "Box2",
          to: "Box6",
        },
        {
          from: "Box6",
          to: "Box3",
        },
        {
          from: "Box6",
          to: "Box4",
        },
        {
          from: "Box6",
          to: "Box5",
        },
        {
          from: "Box3",
          to: "END",
        },
        {
          from: "Box4",
          to: "END",
        },
        {
          from: "Box5",
          to: "END",
        },

        {
          from: "Box1",
          to: "Box4",
        },
        {
          from: "Box1",
          to: "Box3",
        },
      ];

      const $ = go.GraphObject.make;

      const init = () => {
        const myDiagram = $(go.Diagram, "myDiagramDiv", {
          layout: $(go.LayeredDigraphLayout, {
            layerSpacing: 100,
            linkSpacing: 20,
            layeringOption: go.LayeredDigraphLayout.LayerLongestPathSource,
          }),
        });

        myDiagram.nodeTemplateMap.add(
          "",
          $(
            go.Node,
            "Spot",
            new go.Binding("avoidable", "isHidden", h => !h),
            new go.Binding("opacity", "isHidden", h => {
              return h ? 0.1 : 1;
            }),
            $(go.Shape, "RoundedRectangle", {
              fill: "white",
              width: 100,
              height: 100,
              portId: "",
            }),
            $(
              go.TextBlock, // the text label
              new go.Binding("text", "key"),
              {
                verticalAlignment: go.Spot.Center,
                textAlign: "center",
              },
            ),
          ),
        );

        myDiagram.linkTemplate = $(
          go.Link, // the whole link panel
          new go.Binding("opacity", "", data => {
            const nodeFrom = myDiagram.findNodeForKey(data.from);
            const nodeTo = myDiagram.findNodeForKey(data.to);
            return nodeFrom.data.isHidden || nodeTo.data.isHidden ? 0 : 1;
          }),
          { routing: go.Link.Orthogonal, corner: 16, reshapable: true },
          $(
            go.Shape, // the link shape
            { strokeWidth: 1.5 },
          ),
          $(
            go.Shape, // the arrowhead
            { toArrow: "Standard", stroke: null },
          ),
        );

        const model = new go.GraphLinksModel();
        model.nodeDataArray = nodeDataArray;
        model.linkDataArray = linkDataArray;
        myDiagram.model = model;
      };

      window.addEventListener("DOMContentLoaded", init);
    </script>
  </body>
</html>

It looks like the layout is routing the link. Maybe you want to set isRouting: false on your layout to allow links to be routed without considering the hidden node.

I should also mention, maybe instead of changing the node’s opacity, you’d rather set isOngoing: false on your layout and then trigger a relayout when ready. Or set layoutConditions: go.Part.LayoutStandard & ~go.Part.LayoutRemoved on your node and link templates. Read more about layout invalidation here.