Saved link points lost on load

I have a two-way binding for the link points, but something is reseting the points array after the model is loaded and I can’t tell what.

I’ve put some breakpoints to try and debug, and you can see that the saved points (array with length 8) is initially loaded by the binding:

However, at some point later on, it gets reset (to an array of length 6) - triggered by the same transaction that adds the edge to the model (altho I tried removing the transaction and it just gets triggered later on instead)

I do already have two-way location binding on the nodes. I am also pretty sure it is not a layout problem because I put a breakpoint in Diagram.layoutDiagram and it does not get triggered. The link template is this:

$go(go.Link,
  {
    // routing: go.Routing.Orthogonal,
    routing: go.Routing.AvoidsNodes,
    // curve: go.Curve.JumpOver,
    corner: 10,
    reshapable: true,
    resegmentable: true,
    relinkableFrom: true,
    relinkableTo: true,
    fromEndSegmentLength: 20,
    toEndSegmentLength: 20,
    toolTip: linkToolTiptemplate,
    contextMenu: linkMenu,
    layerName: "LinkLayer",
  },
  new go.Binding("points", "points", p => p)
    .makeTwoWay(p => p),

I am looking for clues to investigate why this might be happening.

Have you set the Diagram.layout to anything, and if so, what?

I’m guessing that a layout is happening, since by default, layouts are invalidated and performed at the end of the transaction whenever a node or a link is added or removed or changes visibility. Read more about this at: Layouts | GoJS

I am pretty confident that there is no layout involved when the diagram is first loaded - as I said, I put a breakpoint in Diagram.layoutDiagram and it does not get triggered but there might be a better way to know for sure.

However, I found this really old post and the solution there seems to have fixed the problem… So I set the Link.adjusting to go.LinkAdjusting.Stretch and it now behaves - I am not sure if this is intended since the post was from 11 years ago and it says it was fixed

FYI, Diagram.layoutDiagram is not called when invalid layouts are performed. (With a true argument, it actually invalidates all layouts.)

You can add a “LayoutCompleted” DiagramEvent listener to notice when layouts are finished being performed.

Hello, so after further testing, it appears that setting go.LinkAdjusting.Stretch works most of the time but not always… some edges get reset even with that. I added a “LayoutCompleted” DiagramEvent and it does get called twice but the Diagram.layout is an instance of the base Layout class and I cannot find a way to stop it from being called.

The docs say:

If you have position information for all of the nodes when you load a model, you will typically have data bound the Part.location to some property on your node data. In order to avoid an initial layout causing those saved node positions to be discarded, you can either not set the Diagram.layout to a predefined layout or you can set isInitial to false.

And we don’t set it, so I don’t quite get what’s going on. I also tried manually setting this right as the diagram is created (the default value was false)

myDiagram.layout.isValidLayout = true
myDiagram.layout.isInitial = false;

So you do not set Diagram.layout at all? OK, then the situation is like that of a number of samples, such as FlowChart: Interactive Diagram for Building Flowcharts | GoJS Diagramming Library

Select and reshape a Link, click the “Save” button, optionally make any changes to the diagram that you like, then click the “Load” button to restore the flowchart including the manually just rerouted Link. The Link template does not set Link.adjusting. The “Load” button assumes that all Nodes have real locations.

The situation I described earlier was assuming that you had set Diagram.layout to a typical layout so that the diagram does automatic layouts. By default it would operate whenever you replace the Diagram.model, because they would be all new Nodes and Links, so it makes sense to automatically perform a layout.

But if you had node locations and link routes saved in the model, you would first have to set Layout.isInitial to false (and maybe also Layout.isOngoing) so that the initial automatic layout would not happen.

Do you have Pictures that when their images are loaded cause the Nodes that they are in to change size? That might be causing an automatic layout after the initial phase.

Or do you do anything else after the “InitialLayoutCompleted” DiagramEvent to modify nodes?

Perhaps the issue is in the way the model is created? In all the examples of saving and loading I see, the model is directly created from JSON, but what we do is create an empty GraphLinksModel and load into it using a bunch of addNodeData and addLinkData in a transaction - this is because we save the nodes in a different format and get them async.

And yes, “InitialLayoutCompleted” gets called before all the nodes are added using addNodeData

We do have go.Picture objects - I tried removing them to see if they are the cause but that does not seem to be the case.

Before setting Layout.isOngoing on the default layout to false, the order in breakpoints being hit goes like this:
“InitialLayoutCompleted” → “LayoutCompleted” → “addNodeData” → “LayoutCompleted”

After setting Layout.isOngoing to false the order goes like this but the result is the same
“InitialLayoutCompleted” → “LayoutCompleted” → “addNodeData”

If your loading of models involves the modification of the current Diagram.model rather than building a new model and replacing Diagram.model, then either you need to pretend that it’s a new diagram initialization by calling Diagram.delayInitialization (which is what gojs-react does), or else you can ignore all of the Diagram.initial… properties and handle everything yourself.

I think what you need to do is load all of the nodes, let the transaction complete, and then do another transaction to load the links. That way the addition or modification of nodes won’t happen after the links have been loaded, causing their routes to be invalidated and then recomputed.

Alternatively you could load all nodes and links, and then in your “LayoutCompleted” listener you could iterate through all of the link data and reset Link.points to their intended/saved values. But this scheme won’t work if you expect users to reshape link routes and thus you have a TwoWay Binding on the Link.points property, since any routing of the links would cause their saved “points” lists to be overwritten.

OK, thank you for your help, the solution seems to be:

  1. add the nodes and edges to the model in two separate transactions
  2. set isOngoing to false to the default layout (setting isInitial to false does not seem to affect the issue but we’ll set it anyway)
  3. can then remove the adjusting from the link template