Way to bulk replace model data


#1

In our external properties when the user changes the name we do this in a transaction:

      this.diagram.removeModelChangedListener(this.handleModelChanged);
      model.nodeDataArray = nextProps.nodeDataArray;
      model.linkDataArray = nextProps.linkDataArray;
      this.diagram.addModelChangedListener(this.handleModelChanged);

It works but one side-effect is the selected node goes away plus I’m guessing it’s inefficient. I’m wondering if there is a way to apply the changes from nextProps.nodeDataArray to the current model.nodeDataArray? The only difference between the two is one node will have the name changed but in this code I don’t know which node that is. Any advice on a better way to handle this?

If not then I guess I can get the current selection, do the code above, then re-apply the selection.


#2

Try replacing those two lines with the not-yet-documented calls to:

  model.mergeNodeDataArray(nextProps.nodeDataArray);
  model.mergeLinkDataArray(nextProps.linkDataArray);

NOTE: the GraphLinksModel must be maintaining keys for the links – GraphLinksModel.linkKeyProperty must be the name of the property on the link data.


#3

Walter I added those calls and my handleModelChange is now getting called on commitTransaction() when it wasn’t before and causing problems. I can’t remember why I didn’t want that called but will look into it. Here is my complete code snippet.

    this.diagram.startTransaction('modelerUpdate');
    if (nextProps.dataVersion !== dataVersion) {
      this.diagram.clearSelection();
      model.nodeDataArray = nextProps.nodeDataArray;
      model.linkDataArray = nextProps.linkDataArray;
    }

    if (
      this.hasDataChanges(nodeDataArray, nextProps.nodeDataArray) ||
      this.hasDataChanges(linkDataArray, nextProps.linkDataArray, 'tranData')
    ) {
      this.diagram.removeModelChangedListener(this.handleModelChanged);
      model.mergeNodeDataArray(nextProps.nodeDataArray);
      model.mergeLinkDataArray(nextProps.linkDataArray);
      this.diagram.addModelChangedListener(this.handleModelChanged);
    }

    if (archetypeNodeData !== nextProps.archetypeNodeData) {
      this.diagram.toolManager.clickCreatingTool.archetypeNodeData = nextProps.archetypeNodeData;
    }

    const nextFind = nextProps.find;
    if (find !== nextFind && !!nextFind) {
      this.findParts(nextFind);
    }

    this.diagram.commitTransaction('modelerUpdate');

This is my unique key code which again I need to remember why I did it this way (we moved to a monorepo so the commit “Moved to monorepo” doesn’t help me any).

  makeUniqueKey = array => model => `-${model[array].length + 1}`;
...
    model.nodeKeyProperty = 'id';
    model.linkKeyProperty = 'id';
    model.makeUniqueKeyFunction = this.makeUniqueKey('nodeDataArray');
    model.makeUniqueLinkKeyFunction = this.makeUniqueKey('linkDataArray');

I’ll do some tracking down on my end but if you see anything obvious let me know. We’re on 2.0.9.


#4

Based on your calls to removeModelChangedListener and addModelChangedListener, I imagine you didn’t want your handleModelChange called because you were trying to prevent update cycles. What happens if you place those calls outside the transaction?

You can also prevent cycles by ensuring the model changes aren’t already reflected in your data via comparison, or maybe using some sort of flag to indicate the GoJS model is being updated with current data and not to execute in the handleModelChange function.


#5

I did this and everything seems to be working fine now.

      this.diagram.removeModelChangedListener(this.handleModelChanged);
      this.diagram.startTransaction('modelerUpdate');
      model.mergeNodeDataArray(nextProps.nodeDataArray);
      model.mergeLinkDataArray(nextProps.linkDataArray);
      this.diagram.commitTransaction('modelerUpdate');
      this.diagram.addModelChangedListener(this.handleModelChanged);

#6

jhardy just saw your post after making my last one. I do have checks if data has changed and only update the model if it has.

    if (
      this.hasDataChanges(nodeDataArray, nextProps.nodeDataArray) ||
      this.hasDataChanges(linkDataArray, nextProps.linkDataArray, 'tranData')
    ) {
      this.diagram.removeModelChangedListener(this.handleModelChanged);
      this.diagram.startTransaction('modelerUpdate');
      model.mergeNodeDataArray(nextProps.nodeDataArray);
      model.mergeLinkDataArray(nextProps.linkDataArray);
      this.diagram.commitTransaction('modelerUpdate');
      this.diagram.addModelChangedListener(this.handleModelChanged);
    }

#7

Ok, glad you got it working. I was just referring to checking for changes outside of GoJS in your handleModelChange function, but this method should be fine too.