Traverse updated diagram when new link has been added

Hello,

I have implemented a ModelChanged listener to get notified when new links are added either by the user or programatically:

if (e.change === go.ChangedEvent.Insert && e.propertyName === "linkDataArray") ...

Now within that listener, I traverse the diagram, and I would like the diagram to already include the new link. For example, when using something like node.findLinksOutOf(), the collection should include the newly added link. However, this is not the case currently. In the ModelChanged listener, the link is not yet added to the diagram as it seems.

Is there a way to achieve the above described behaviour?
Using setTimeout seems to work, but maybe there is a better way? Note that I cannot used the onLinkDrawn handler as I also need to handle programmatically added links.

This is our use case:
grafik

The KT value of a node is calculated based on parameters of all successor nodes. So whenever a link is added (or deleted), I need to recalculate the KT value (as successors might have changed). And in order to recalculate the KT value I need to traverse the (updated) diagram.

I think what happens is that the Link is added and is present in the Diagram. Then it is connected to two ports (probably, but not necessarily for disconnected links), one at a time.

Okay, but the issue remains the same. I cannot traverse the updated diagram in the ModelChanged handler. Ist using setTimeout the only solution?

You could check the “from” and “to” keys to find the connected Nodes. https://gojs.net/latest/api/symbols/GraphLinksModel.html#getFromKeyForLinkData

In fact I would think you would also want to check for changes to which nodes/ports a link connects with: https://gojs.net/latest/intro/changedEvents.html#ModelPropertyChanges

Another issue to consider is that it might be too expensive to traverse the graph and do your computations each time a link is added (or removed?) or (re-)connected. It might be better to wait until the graph is complete anyway.

This sounds good. If the user deletes 5 links at once, I do not want to trigger the recalculations 5 times. How would I wait until the graph is complete? I could set a flag (linksModified = true) in the modelChanged listener and then do the calculations in a setTimeout() if the flag is true. Is this what you’re suggesting, or is there some other mechanism I could use?

Additionally I was wondering how to handle undo of link modifications,

  • I want to do recalculations whenever links have been added/deleted/modified/(relinked) (either manually, by copy+paste or programmatically).
  • I want to do recalculations when undo was performed for links which have been added/deleted/modified/(relinked).
  • I do not want to do recalculations when the node positions are moved or other unrelated changes are done.

It seems that an undo of a link deletion does not trigger a go.ChangedEvent.Insert for the linkDataArray, is this correct?
Do I instead need to go through the changes in the transaction event and manually check if there has been a link deletion?

Yes, what you suggest for a general approach sounds reasonable. Only you know when you want to update your graph with those recomputations.

However, that would require performing a separate transaction just to record those data changes and update the diagram again. I suggest another approach. Have a Changed listener on the Model detect when the graph has changed its structure and set a flag. (And don’t bother to check if the flag is already set.) Then at the end of the transaction, if the flag is set, do the recomputation and update the data and clear the flag. You can do that in your Changed listener when e.change === go.ChangedEvent.Transaction && e.propertyName === "CommittingTransaction".

Regarding undo & redo: are your calculations stored in the model using methods such as Model.set? And are your calculations dependent only on the state in the model? (I.e., not on what the weather is at the moment or the last bit of the current time.) If so, then you don’t need to do anything – an undo will restore those calculated values too.

Thank you, using “CommittingTransaction” works perfectly, exactly what I was looking for!