Adding Link in Model ChangedEvent

I have a scenario when adding a node to the diagram where I also need to add a self-looping link to that node. I need to do that when the Model ChangedEvent fires and propertyName is ‘CommittedTransation’. I iterate over the modified nodes and create a link if it’s needed. I’m not sure the best practice to do this?

If I do a setTimeout() it works but seems hokey.
setTimeout(() => model.addLinkData(buildLinkData(transitionData)), 0);

If I use a transaction the Chrome tab locks up maybe in infinite loop?

          model.startTransaction('addLink');
          model.addLinkData(buildLinkData(transitionData));
          model.commitTransaction('addLink');

Here it is in the context of the code

  updateNodes = event => {
    const {
      formArrayPush,
      formArrayRemove,
      formChange,
      form,
      formValues: { nodes }
    } = this.props;
    const { model } = event;
    const changes = JSON.parse(model.toIncrementalJson(event));
    const modified = changes.modifiedNodeData || [];
    const inserted = changes.insertedNodeKeys || [];
    const removed = changes.removedNodeKeys || [];

    modified.forEach(item => {
      if (inserted.includes(item.id)) {
        let data = { ...nodeModel };
        const { transition } = item;
        const idx = Math.abs(item.id);
        const goData = model.findNodeDataForKey(item.id);

        // We are part of an undo/redo
        if (model.modelData._removedNodes[item.id]) {
          data = model.modelData._removedNodes[item.id];
          delete model.modelData._removedNodes[item.id];
        } else {
          data = {
            ...this.setNodeData(item, data, idx),
            id: item.id,
            type: item.category,
            locationX: item.location.x,
            locationY: item.location.y,
            width: item.width,
            height: item.height
          };
        }

        formArrayPush(form, 'nodes', data);
        if (transition) {
          const transitionData = {
            ...transition,
            fromNodeId: data.id,
            toNodeId: data.id
          };

          // Works but maybe bad practice?
          setTimeout(() => model.addLinkData(buildLinkData(transitionData)), 0);

          // Locks up Chrome tab maybe infinite loop?
          model.startTransaction('addLink');
          model.addLinkData(buildLinkData(transitionData));
          model.commitTransaction('addLink');
        }
        Object.assign(goData, buildNodeData(data));
        model.updateTargetBindings(goData);
      }
...

What would be the recommended way to add a link in a Model ChangedEvent?

Why can’t you just add the link at the same time that you are adding a node, if the situation calls for it? In other words, why bother to implement a Changed listener at all?

In general if you are getting an infinite loop, I suggest you debug it to see what’s on the stack.

I’m not creating the node, GoJS is on a double-click using archetypeNodeData and I use the ChangedEvent to modify the data. For a certain node I need to manually add a transition to it. In the previous version of our app we had all kinds of GoJS events and I was able to get rid of all them except selection changed by using the ChangedEvent which handles inserts, deletes, updates, and undo/redo. That’s why I use a Changed listener and why I need to manually add a link in it.

OK. I’m concerned that trying to execute a transaction in a Transaction ChangedEvent would be a bad idea. You must have that idea too now, given your experience.

One possibility would be to try to add the link when a node is added –not upon a Transaction ChangedEvent but on an Insert ChangedEvent. The addition would be within the ongoing transaction.

The other possibility is what you suggest – delay the addition of the link until after this transaction is all done. But that would need to be performed within a new transaction. That has the feature, which might be a disadvantage, that it’s a separate transaction that could be undone.

Added this to my ChangedEvent handler

    if (changeType === go.ChangedEvent.Insert && modelChange === 'nodeDataArray') {
      this.onNodeInserted(event);
    }

Then created this function

  onNodeInserted = event => {
    const { newValue: node, model } = event;
    const { transition } = node;

    if (transition) {
      const transitionData = {
        ...transition,
        fromNodeId: node.id,
        toNodeId: node.id
      };

      model.addLinkData(buildLinkData(transitionData));
    }
  };

This works and I even have the new link in the CommittedTransaction processing I do and undo does both the node and link!