Nodes and links reverting coordinates after editing data

After migrating to the gojs-angular 2 version I have noticed a strange behavior I did not have before.

If I move a node, then edit one of its properties in the inspector, the node reverts back to its position automatically.

This also happens with links. I have no idea where to look and it does not make sense to me since I only produce a new state based on the property value that has changed in the inspector, I don’t touch coordinates.

Any idea where I should at and what could possibly cause this ?

Note that if I unselect a node after dragging it, then reselect it and edit one of the properties in the inspector, then the node does not revert back to its previous position.

screenshot2

Hello, thanks for flagging this. I will look more into it tomorrow (It’s late here in Eastern US)

Can you show me your code the propagates data from Inspector Component to your App-level component? That may help me pinpoint the issue

relevant code only:

inspector :

 Label: <textarea class="form-control" name="label" [ngModel]="selectedNode['label']"
      (change)="emitChange($event, 'label')"></textarea>
  @Output()
  public formChange: EventEmitter<any> = new EventEmitter<any>();


  @Input()
  selectedNode: go.ObjectData;

  public emitChange(e, prop): void {
    this.formChange.emit({ prop, newVal: e.target.value });
  }

handler in component:

 public handleInspectorChange(newData): void {
    const path = newData.prop;
    const value = newData.newVal;

    this.state = produce(this.state, draft => {
      const data = draft.selectedNode;
      data[path] = value;
      const key = data.key;

      let idx = draft.diagramNodeData.findIndex(nd => nd['key'] === key);
      if (idx >= 0) {
        draft.diagramNodeData[idx] = data;
      } else {
        idx = draft.diagramLinkData.findIndex(nd => nd['key'] === key);
        draft.diagramLinkData[idx] = data;
      }

      draft.skipsDiagramUpdate = false; // we need to sync GoJS data with this new app state, so do not skips Diagram update

      this.hasChanged = true;
    });
  }

What’s the format of your node data, and what do your go.Binding(s) look like that handle node location?

Also, do you have a Diagram.layout defined? Is it’s isOngoing property set to false?

Aha! I suspect I know the issue.

When an <input> of the Inspector changes, Inspector Component incrementally updates the data of the node based on whatever it has in its nodeData property (plus the prop / val change of the <input> that changed). Inspector.nodeData is received via AppComponent.selectedNodeData. It is possible Inspector’s nodeData property is out of sync with what AppComponent has, since, if you’re using the gojs-angular-basic sample, we do not store location data in the data, and as such, we do not modify AppComponent.selectedNodeData when a node is moved.

How do we fix this?

First, make sure in your node / link template(s) you have bound the location property with a TwoWay data binding, like this

// The Node.location comes from the "loc" property of the node data,
// converted by the Point.parse static method.
// If the Node.location is changed, it updates the "loc" property of the node data,
// converting back using the Point.stringify static method.
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify)

Then, we need to make sure that when a node is moved, since we care about its location, whatever Inspector.nodeData is holding knows about the change and is updated (i.e., we must update AppComponent.selectedNodeData.

There are a few ways to do this, but this most future-proof one is to modify the diagramModelChange function of AppComponent to check if one of the modified node data’s is what the Inspector is currently tracking. If so, we’ll update AppComponent.selectedNodeData to this new state (this has the advantage of updating Inspector.nodeData now whenever any data property of what the Inspector’s inspected node changes.

So, add this within the produce section of your diagramModelChange function:

  // If one of the modified nodes was the selected node used by the inspector, update the inspector selectedNodeData object
  const modifiedNodeDatas = changes.modifiedNodeData;
  if (modifiedNodeDatas && draft.selectedNodeData) {
    for (let i = 0; i < modifiedNodeDatas.length; i++) {
      const mn = modifiedNodeDatas[i];
      const nodeKeyProperty = appComp.myDiagramComponent.diagram.model.nodeKeyProperty as string;
      if (mn[nodeKeyProperty] === draft.selectedNodeData[nodeKeyProperty]) {
        draft.selectedNodeData = mn;
      }
    }
  }

This is actually probably the behavior we should publish in gojs-angular-basic to be more thorough, so thank you for bringing this to my attention!

Perfect, it works :) Thank you.