Relinking Tool Rollback

Hi,

I would like to know under what conditions the relinking tool will rollback changes done inside the reconnectLink function.

The reason I ask is because I have overridden the reconnectLink function to add a node to the canvas when you drag the end of the link to an empty space on the canvas. However this change is rolled back along with any changes made to the link.

The link itself is not relinked to the original node either, but moved back to the same position it was before. Giving the user the illusion that it was re-linked.

Thank you.

I’m not sure I understand your description of the situation that you have.

What is most common for what I think you are trying to do is to override the RelinkingTool.doNoRelink method.
RelinkingTool | GoJS API

Here’s a complete example:

<!DOCTYPE html>
<html>
<head>
  <title>Minimal GoJS Sample</title>
  <!-- Copyright 1998-2022 by Northwoods Software Corporation. -->
</head>
<body>
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>

  <script src="https://unpkg.com/gojs"></script>
  <script id="code">
const $ = go.GraphObject.make;

const myDiagram =
  $(go.Diagram, "myDiagramDiv",
    {
      layout: $(go.ForceDirectedLayout),
      "relinkingTool.doNoRelink": function(link, toend) {
        const pt = this.diagram.lastInput.documentPoint;
        const ndata = { text: "new node", loc: go.Point.stringify(pt) };
        this.diagram.model.addNodeData(ndata);
        const node = this.diagram.findNodeForData(ndata);
        if (toend) {
          link.toNode = node;
        } else {
          link.fromNode = node;
        }
        this.transactionResult = "with new node";
      },
      "undoManager.isEnabled": true
    });

myDiagram.nodeTemplate =
  $(go.Node, "Auto",
    new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
    $(go.Shape,
      { fill: "white", portId: "", cursor: "pointer", fromLinkable: true, toLinkable: true },
      new go.Binding("fill", "color")),
    $(go.TextBlock,
      { margin: 8 },
      new go.Binding("text"))
  );

myDiagram.linkTemplate =
  $(go.Link,
    { relinkableFrom: true, relinkableTo: true },
    $(go.Shape, { strokeWidth: 2 }),
    $(go.Shape, { toArrow: "Standard" })
  );

myDiagram.model = new go.GraphLinksModel(
[
  { key: 1, text: "Alpha", color: "lightblue" },
  { key: 2, text: "Beta", color: "orange" },
  { key: 3, text: "Gamma", color: "lightgreen" },
  { key: 4, text: "Delta", color: "pink" }
],
[
  { from: 1, to: 2 },
  { from: 1, to: 3 },
  { from: 2, to: 2 },
  { from: 3, to: 4 },
  { from: 4, to: 1 }
]);
  </script>
</body>
</html>

You might also want to do a similar thing when the user tries to draw a new link and there’s no existing node that it can validly connect with. In that case you’ll want to override the LinkingTool.doNoLink method.
LinkingTool | GoJS API

Sorry, maybe I am not explaining myself properly.

My relinking tool overrides the reconnectLink function. When the endpoint of the link is dropped on a space where there is no existing node, it creates a node at the endpoint and links that node to the link endpoint. The linking tool has the same implementation and it works well there.

My issue is that the node that I add in the reconnectLink gets deleted and the link itself moves back to where it was. However it does not get reconnected to the node to which it was connected before.

What could cause this?

I guess I didn’t make clear that the reconnectLink method is called when the link being relinked is connected to a new port. The doNoLink method is called when reconnecting the link with no new port, as would be the case when the user does a mouse-up away from any ports.

I do not understand. The reconnectLink function does fire because the Node is created (I have set the property isUnconnectedLinkValid to true), however it is directly deleted afterwards. I have also set a breakpoint in the function and I can see it processes. The doNoLink is not firing at all. I also have a model change listener, which fires with removednodekey rather than insertednodekey.

Ah, LinkingTool.isUnconnectedLinkValid matters. I did not know I had to use it in the sample I gave you above.

OK, the idea is almost the same as in the regular case with overriding doNoRelink. You just have to handle the case where the newnode and newport are not null.

      "relinkingTool.isUnconnectedLinkValid": true,
      "relinkingTool.reconnectLink": function(link, newnode, newport, toend) {
        if (!newnode) {
          const pt = this.diagram.lastInput.documentPoint;
          const ndata = { text: "new node", loc: go.Point.stringify(pt) };
          this.diagram.model.addNodeData(ndata);
          newnode = this.diagram.findNodeForData(ndata);
          newport = newnode.port;
        }
        if (toend) {
          link.toNode = newnode;
          link.toPortId = newport.portId | "";
        } else {
          link.fromNode = newnode;
          link.fromPortId = newport.portId | "";
        }
      },

Thank you for your responses walter.

I have discovered that i had another listener on properties which i set in reconnectlink. The listener would trigger a change detection in angular which would update the diagram and revert any changes i made.