Can I clean up all these event listeners?

For our next release we are going to implement copy/paste and undo in our product. One of our challenges is our data format doesn’t match GoJS model so we can’t just use 2-way binding to make it easy. Right now we listen to a bunch of events and create and manipulate our data as needed. We also override several GoJS methods because we need more control - for example prompt the user before deleting. Now getting to undo and looking at this thread with my POC, it seems we need a third way to handle events.

So now it seems our way of handling events is getting out of control and part of it is because our model doesn’t match the GoJS. Looking at what we have, is there a way to possibly handle everything one way? Could listening to model changes do everything we want, or most of it? Here are our current events with undo adding a third way to it.

        // go.Diagram events
    goDiagram.addDiagramListener('ChangedSelection', function (e) {
        me.onChangedSelection(e);
    });
    goDiagram.addDiagramListener('PartResized', function (e) {
        me.onPartResized(e);
    });
    goDiagram.addDiagramListener('InitialLayoutCompleted', function (e) {
        me.onInitialLayoutCompleted(e);
    });
    goDiagram.addDiagramListener('SelectionMoved', function (e) {
        me.onSelectionMoved(e);
    });
    goDiagram.addDiagramListener('LinkReshaped', function (e) {
        me.onLinkReshaped(e);
    });
    goDiagram.addDiagramListener('LinkRelinked', function (e) {
        me.onLinkRelinked(e);
    });
    goDiagram.addDiagramListener('TextEdited', function (e) {
        me.onTextEdited(e);
    });
    goDiagram.addDiagramListener('ObjectContextClicked', function (e) {
        me.onContextClick(e);
    });
    goDiagram.addDiagramListener('ObjectDoubleClicked', function (e) {
        me.onDoubleClick(e);
    });
    goDiagram.addDiagramListener('BackgroundContextClicked', function (e) {
        me.onContextClick(e);
    });

    // go.Diagram overrides instead of events because we need more control
    goDiagram.commandHandler.deleteSelection = function () {
        me.onSelectionDeleting(goDiagram, this);
    };
    diagram.commandHandler.pasteSelection = function(pos) {
        me.onClipboardPasted(pos, diagram, this);
    };
    goDiagram.toolManager.clickCreatingTool.insertPart = function (loc) {
        return me.onPartCreated(loc, goDiagram, this);
    };
    goDiagram.toolManager.linkingTool.insertLink = function (fromnode, fromport, tonode, toport) {
        return me.onLinkDrawn(fromnode, fromport, tonode, toport, goDiagram, this);
    };

Any direction on how to clean up our events would be appreciated or pointing to some examples. I’m currently going to study this one some more:

One thing to add is going to 1.6 beta would be fine as long as it’s going to be release within the next few months. I don’t know if 1.6 adds anything that would help.

I’m curious why you thought you couldn’t work with the models that GoJS normally uses.

Most programmers in your situation would not establish any (or certainly not many) DiagramEvent listeners. Instead they would establish just one listener, a model Changed listener. Take a look at the Update Demo, Update Demo GoJS Sample, and how it watches for Transaction ChangedEvents that are ChangedEvent.isTransactionFinished, and then scans through the Transaction for model ChangedEvents that are relevant to the app.

1.6 may have some features that you could use. First, a minor convenience: there’s now a “ModelChanged” Diagram listener, which registers a listener on the Diagram.model, but automatically unregisters from the old model and reregisters on the new model when the diagram’s model is replaced.

Second, there’s a new method, Model.toIncrementalJson, Model | GoJS API, which helps support app architecture designs where you want to send incremental model changes to the server, rather than sending the whole model via Model.toJson. You still need to process them somehow on the server, but it avoids the payload of sending the whole model and avoids having to figure out what differences happened in a transaction.

walter - to clarify we do use the GoJS model but our data which supports our old Java UI doesn’t always match up what GoJS wants. For example for links we store only the midpoints in the DB whereas GoJS also needs the start and end points. It’s situations like these where we need to change our model whenever the GoJS model changes.

I did post the Update Demo in my first post which I’m looking at. Sounds like I’m on the correct path to use model events to get rid of as many diagram events as I can.

I’ve been playing around with model listeners to try and get rid of several of my other listeners. What’s your recommendation for this scenario? When the user double-clicks for ClickCreating, I need to set a bunch of dynamic model data for the node including the category. The Changed Events guide says as a rule you shouldn’t modify model data in a transaction. Is there a model event that gets called where I can set this before the transaction starts?

If you take a look at the documentation for the ClickCreatingTool, ClickCreatingTool | GoJS API, it says that it conducts a transaction and raises the “PartCreated” DiagramEvent.

If you look at the DiagramEvent documentation, DiagramEvent | GoJS API, for the “PartCreated” DiagramEvent it says the the DiagramEvent.subject is the new Part and that the event is raised with a transaction.

But it sounds like you want to customize the data before the node is created. Clearly you can set whatever properties you want on the ClickCreatingTool.archetypeNodeData before the tool runs, including when the user changes “modes”, if your app is organized that way.

If you really want to determine the node data just as the node data is copied and added to the model, override ClickCreatingTool.insertPart to set whatever properties you want on the ClickCreatingTool.archetypeNodeData and then call the base method.

I’m currently overriding insertPart as one of my “fake” listeners and setting the node data. So maybe it’s best to keep this one as is instead of using a model event?

    goDiagram.toolManager.clickCreatingTool.insertPart = function (loc) {
        return me.onPartCreated(loc, goDiagram, this);
    };

    goDiagram.toolManager.clickCreatingTool.archetypeNodeData = node.nodeData;
    part = go.ClickCreatingTool.prototype.insertPart.call(scope, loc);

Still moving along and still more questions. For a move event of a single node there can be a lot of changes. Iterating over all these and updating our data is a lot of overhead when really I just care about the last one. Is it safe to just get the last change? If multiple nodes and links are selected is it always the last change for each one? This seems like a lot more work than just using the SelectionMoved event, but since you said most people just the single ModelChange event, I’m trying to do that best practice. If the ModelChange event object has what I need w/o having to iterate over all the changes please let me know.

*  CommittedTransaction:  Move
 {"change":"ChangedEvent.Transaction","propertyName":"CommittedTransaction","modelChange":"","newParam":null,"newValue":null,"oldParam":null,"oldValue":"Move"}
     {"change":"!d position: Node#1312(Node 1)  old: Point(90,60)  new: Point(90.5,60.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(90,60)  new: Point(90.5,60.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(90.5,60.5)  new: Point(120.5,90.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(90.5,60.5)  new: Point(120.5,90.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(120.5,90.5)  new: Point(135.5,105.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(120.5,90.5)  new: Point(135.5,105.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(135.5,105.5)  new: Point(165.5,135.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(135.5,105.5)  new: Point(165.5,135.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(165.5,135.5)  new: Point(195.5,150.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(165.5,135.5)  new: Point(195.5,150.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(195.5,150.5)  new: Point(225.5,180.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(195.5,150.5)  new: Point(225.5,180.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(225.5,180.5)  new: Point(270.5,210.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(225.5,180.5)  new: Point(270.5,210.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(270.5,210.5)  new: Point(315.5,240.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(270.5,210.5)  new: Point(315.5,240.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(315.5,240.5)  new: Point(360.5,255.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(315.5,240.5)  new: Point(360.5,255.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(360.5,255.5)  new: Point(390.5,285.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(360.5,255.5)  new: Point(390.5,285.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(390.5,285.5)  new: Point(435.5,300.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(390.5,285.5)  new: Point(435.5,300.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(435.5,300.5)  new: Point(450.5,315.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(435.5,300.5)  new: Point(450.5,315.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(450.5,315.5)  new: Point(495.5,330.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(450.5,315.5)  new: Point(495.5,330.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(495.5,330.5)  new: Point(510.5,345.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(495.5,330.5)  new: Point(510.5,345.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(510.5,345.5)  new: Point(540.5,360.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(510.5,345.5)  new: Point(540.5,360.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(540.5,360.5)  new: Point(570.5,375.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(540.5,360.5)  new: Point(570.5,375.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(570.5,375.5)  new: Point(600.5,405.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(570.5,375.5)  new: Point(600.5,405.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(600.5,405.5)  new: Point(660.5,420.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(600.5,405.5)  new: Point(660.5,420.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(660.5,420.5)  new: Point(705.5,435.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(660.5,420.5)  new: Point(705.5,435.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(705.5,435.5)  new: Point(750.5,450.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(705.5,435.5)  new: Point(750.5,450.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(750.5,450.5)  new: Point(795.5,465.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(750.5,450.5)  new: Point(795.5,465.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(795.5,465.5)  new: Point(840.5,480.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(795.5,465.5)  new: Point(840.5,480.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(840.5,480.5)  new: Point(885.5,480.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(840.5,480.5)  new: Point(885.5,480.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(885.5,480.5)  new: Point(900.5,495.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(885.5,480.5)  new: Point(900.5,495.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(900.5,495.5)  new: Point(930.5,495.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(900.5,495.5)  new: Point(930.5,495.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(930.5,495.5)  new: Point(945.5,495.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(930.5,495.5)  new: Point(945.5,495.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(945.5,495.5)  new: Point(960.5,495.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(945.5,495.5)  new: Point(960.5,495.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(960.5,495.5)  new: Point(960.5,510.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(960.5,495.5)  new: Point(960.5,510.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(960.5,510.5)  new: Point(975.5,510.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(960.5,510.5)  new: Point(975.5,510.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location
     {"change":"!d position: Node#1312(Node 1)  old: Point(975.5,510.5)  new: Point(990.5,510.5)","propertyName":"position","modelChange":""}
     Node 126:WPDS ChangedEvent.Property position
     {"change":"!d location: Node#1312(Node 1)  old: Point(975.5,510.5)  new: Point(990.5,510.5)","propertyName":"location","modelChange":""}
     Node 126:WPDS ChangedEvent.Property location

Well, whether you can ignore all but the last “position” ChangedEvent depends on your purpose.

Remember that each property ChangedEvent has a ChangedEvent.oldValue, which would be not be representative of the whole transaction if you simply took the last one.

If you would normally be using Model.toJson, you might be interested in Model.toIncrementalJson in version 1.6.

I’m put in Model.toIncrementalJson and it said I needed a linkKeyProperty to work correct. I added it and key is always a unique value on our model data.

    me.goDiagram.model.linkKeyProperty = 'key';

After doing this I get call stack exceptions using 1.6 beta7

Uncaught RangeError: Maximum call stack size exceeded w.ub @ go-debug.js:39Ce @ go-debug.js:341K.writeJsonValue.K.Du @ go-debug.js:336Ce @ go-debug.js:341K.writeJsonValue.K.Du @ go-debug.js:336Ce @ go-debug.js:341K.writeJsonValue.K.Du @ go-debug.js:336Ce @ go-debug.js:341K.writeJsonValue.K.Du @ go-debug.js:336Ce @ go-debug.js:341

What are all of the properties on your data objects (both node data and link data)? Might there be a circular reference in them?

I had an Ext JS object in there and removed it and all is well. When I move a node is doesn’t give much info on the details.

{ “class”: “go.GraphLinksModel”, “incremental”: 1, “linkKeyProperty”: “key”}

Here is my log call

    Ext.log('    ' + goDiagram.model.toIncrementalJson(e));

Did a property on the node data object actually change?

Here is my node data object which has location, width, and height.

    {
    "parent" : "ext-comp-1239",
    "key" : "-1:AUTOGEN",
    "location" : {
        "K" : 300,
        "L" : 225,
        "Na" : false
    },
    "width" : 90,
    "height" : 90,
    "text" : "Node 2",
    "category" : "activitynode",
    "iconText" : "R",
    "iconVisible" : true,
    "templateSource" : "",
    "templateVisible" : false,
    "headerTitle" : "Activity",
    "nodeColor" : "#129be2",
    "nodeStateColor" : "#666666",
    "stateColor" : "#666666"
}

Here is the output when I move and resize that node.

*  CommittedTransaction:  Move
     {"change":"ChangedEvent.Transaction","propertyName":"CommittedTransaction","modelChange":"","newParam":null,"newValue":null,"oldParam":null,"oldValue":"Move"}
     { "class": "go.GraphLinksModel", "incremental": 1,
  "linkKeyProperty": "key"}
 *  CommittedTransaction:  Resizing
     {"change":"ChangedEvent.Transaction","propertyName":"CommittedTransaction","modelChange":"","newParam":null,"newValue":null,"oldParam":null,"oldValue":"Resizing"}
     { "class": "go.GraphLinksModel", "incremental": 1,
  "linkKeyProperty": "key"}

Some more data. Relinking and updateNode (changing text) gives the updated data but LinkShaping does not.

*  CommittedTransaction:  Relinking
     {"change":"ChangedEvent.Transaction","propertyName":"CommittedTransaction","modelChange":"","newParam":null,"newValue":null,"oldParam":null,"oldValue":"Relinking"}
     { "class": "go.GraphLinksModel", "incremental": 1,
  "linkKeyProperty": "key",
  "modifiedLinkData": [ {"parent":"ext-comp-1385", "key":"152:WPDS", "midpoints":{"Na":false, "o":[], "H":0, "ec":null, "Kj":null, "fa":null}, "category":"namedlink", "labelVisible":true, "text":"CW", "from":"101:WPDS", "to":"103:WPDS", "linkColor":"#999", "tranVisible":true, "strokeDashArray":null} ]}
*  CommittedTransaction:  LinkReshaping
     {"change":"ChangedEvent.Transaction","propertyName":"CommittedTransaction","modelChange":"","newParam":null,"newValue":null,"oldParam":null,"oldValue":"LinkReshaping"}
     { "class": "go.GraphLinksModel", "incremental": 1,
  "linkKeyProperty": "key"}
*  CommittedTransaction:  updateNode
     {"change":"ChangedEvent.Transaction","propertyName":"CommittedTransaction","modelChange":"","newParam":null,"newValue":null,"oldParam":null,"oldValue":"updateNode"}
     { "class": "go.GraphLinksModel", "incremental": 1,
  "linkKeyProperty": "key",
  "modifiedNodeData": [ {"parent":"ext-comp-1227", "key":"102:WPDS", "location":{"class":"go.Point", "x":346, "y":91}, "width":90, "height":90, "text":"Node 222221", "category":"activitynode", "iconText":"R", "iconVisible":true, "templateSource":"", "templateVisible":false, "headerTitle":"Activity", "nodeColor":"#129be2", "nodeStateColor":"#666666", "stateColor":"#666666"} ]}

The incremental JSON-format text produced for a Transaction ChangedEvent will dump all of the properties for the modified data object. (Some exceptions apply regarding which property/value pairs are written out.)

I’m not sure what the problem is. It looks as if you are printing the incremental JSON-formatted text of an empty transaction, rather than of the transaction that just finished.

The State Chart Incremental sample, State Chart With Incremental Saves, does this:

          "ModelChanged": function(e) {
            if (e.isTransactionFinished) {
              // this records each Transaction as a JSON-format string
              showIncremental(myDiagram.model.toIncrementalJson(e));
            }
          },

Here is the code that produces the logging in my previous post.

onBoxReady: function() {
   ...
   goDiagram.addModelChangedListener(function(e) {
        me.onModelChanged(e);
    });

},

onModelChanged: function(e) {
    var me = this,
        diagram = me.lookupReference('diagram'),
        goDiagram = diagram.goDiagram,
        txn;

    if (!e.isTransactionFinished) {
        return;
    }

    // Add entries into the log
    var changes = e.toString();
    if (changes[0] !== '*') {
        changes = '    ' + changes;
    }
    Ext.log(changes);

    Ext.log('    ' + Ext.encode({
        change: e.change.toString(),
        propertyName: e.propertyName,
        modelChange: e.modelChange,
        newParam: e.newParam,
        newValue: e.newValue,
        oldParam: e.oldParam,
        oldValue: e.oldValue
    }));
    Ext.log('    ' + goDiagram.model.toIncrementalJson(e));

},

We’ll have to see if we can reproduce the problem. It would help if you could reproduce the problem in some existing sample.

You can see what we think are typical results in the State Chart With Incremental Saves sample, which is just the State Chart sample augmented with logging the incremental model changes when transactions are finished.

One difference I noticed is you example has nodeKeyProperty in the output but mine doesn’t I don’t know if that could be the problem but I’m configuring mine.

    me.goDiagram.model = new go.GraphLinksModel(me.nodes, me.links);
    me.goDiagram.model.linkKeyProperty = 'key';
    me.goDiagram.model.nodeKeyProperty = 'key';

I’ll see if I can make an example on Monday.

Here is my node data

[{
        "parent" : "ext-comp-1226",
        "key" : "101:WPDS",
        "location" : {
            "K" : 106,
            "L" : 91,
            "Na" : false
        },
        "width" : 90,
        "height" : 90,
        "text" : "Node 1",
        "category" : "activitynode",
        "iconText" : "R",
        "iconVisible" : true,
        "templateSource" : "",
        "templateVisible" : false,
        "headerTitle" : "Activity",
        "nodeColor" : "#129be2",
        "nodeStateColor" : "#666666",
        "stateColor" : "#666666"
    }, {
        "parent" : "ext-comp-1227",
        "key" : "102:WPDS",
        "location" : {
            "K" : 346,
            "L" : 91,
            "Na" : false
        },
        "width" : 90,
        "height" : 90,
        "text" : "Node 2",
        "category" : "activitynode",
        "iconText" : "R",
        "iconVisible" : true,
        "templateSource" : "",
        "templateVisible" : false,
        "headerTitle" : "Activity",
        "nodeColor" : "#129be2",
        "nodeStateColor" : "#666666",
        "stateColor" : "#666666"
    }, {
        "parent" : "ext-comp-1228",
        "key" : "103:WPDS",
        "location" : {
            "K" : 346,
            "L" : 271,
            "Na" : false
        },
        "width" : 90,
        "height" : 90,
        "text" : "Node 3",
        "category" : "activitynode",
        "iconText" : "R",
        "iconVisible" : true,
        "templateSource" : "",
        "templateVisible" : false,
        "headerTitle" : "Activity",
        "nodeColor" : "#129be2",
        "nodeStateColor" : "#666666",
        "stateColor" : "#666666"
    }
]

And link data

[{
        "parent" : "ext-comp-1229",
        "key" : "152:WPDS",
        "midpoints" : {
            "__gohashid" : 1319,
            "Na" : false,
            "o" : [],
            "H" : 0,
            "ec" : null,
            "Kj" : null,
            "fa" : null
        },
        "category" : "namedlink",
        "labelVisible" : true,
        "text" : "CW",
        "from" : "101:WPDS",
        "to" : "102:WPDS",
        "linkColor" : "#999",
        "tranVisible" : true,
        "strokeDashArray" : null
    }
]