Obtaining information from a links connected node after it's deleted

I currently need to obtain information about a node when it’s deleted, as well as from the nodes connected to a link when the link is deleted. I have the following code:

myDiagram.addModelChangedListener(function(evt) {
    // ignore unimportant Transaction events
    if (!evt.isTransactionFinished) return;
    var txn = evt.object;  // a Transaction
    if (txn === null) return;
    // iterate over all of the actual ChangedEvents of the Transaction
    txn.changes.each(function(e) {
        // ignore any kind of change other than adding/removing a node
        if (e.modelChange === "nodeDataArray") {
            // record node insertions and removals
            if (e.change === go.ChangedEvent.Insert) {
                console.log(evt.propertyName + " added node with id: " + e.newValue.id);
            } else if (e.change === go.ChangedEvent.Remove) {
                console.log(evt.propertyName + " removed node with id: " + e.oldValue.id);
            }
        }
        if (e.modelChange === "linkDataArray")
        {
            if (e.change === go.ChangedEvent.Insert) {
                console.log(evt.propertyName + " added link between nodes : " + myDiagram.findNodeForKey(e.newValue.from).data.id + " and " + myDiagram.findNodeForKey(e.newValue.to).data.id);
            } else if (e.change === go.ChangedEvent.Remove) {
                console.log(evt.propertyName + " removed link between nodes: " + myDiagram.findNodeForKey(e.oldValue.from).data.id + " and " + myDiagram.findNodeForKey(e.oldValue.to).data.id);
            }
        }
    });
});

This works fine. However, there is one problem. When I delete a node and the connected links are automatically cleaned up, it’s not able to find the node that was just deleted. I assume this is because links are being cleaned up AFTER the node is gone. Is there a way to get around this? One possible solution I am thinking about is forcing connected links to clean up first when you start a deletion, but I’m unsure about how to go about doing this.

I suppose we could change the order in which parts are removed from the diagram, but that would be a bit complicated to implement due to the complexities that occur when groups are involved. And I think the task becomes impossible when link label nodes are involved.

Yes, it’s too late to call Diagram.findNodeForKey or even Model.findNodeDataForKey. But you might be able to find the removed node data object in the other ChangedEvents of the transaction.

So I suppose you could run through the Transaction.changes list twice: once to find and remember all of the node data objects that had been removed, and once to look at all of the link data objects. The latter would have references to nodes that might still be in the model or that might have been removed but that you remembered in the first pass.

I attempted to do it like this:

var deletedNodes = [];

// iterate over all of the actual ChangedEvents of the Transaction
txn.changes.each(function(e) {
    // ignore any kind of change other than adding/removing a node
    if (e.modelChange === "nodeDataArray") {
        // record node insertions and removals
        if (e.change === go.ChangedEvent.Insert) {
            console.log(evt.propertyName + " added node with id: " + e.newValue.id);
        } else if (e.change === go.ChangedEvent.Remove) {
            console.log(evt.propertyName + " removed node with id: " + e.oldValue.id);
            var shallowNode = {
                key : e.oldValue.key,
                id : e.oldValue.id
            }

            var copy = JSON.parse(JSON.stringify(shallowNode));

            deletedNodes.push(jQuery.extend(true, {}, e.oldValue));
        }
    }
});

txn.changes.each(function(e) {
    if (e.modelChange === "linkDataArray")
    {
        if (e.change === go.ChangedEvent.Insert) {
            console.log(evt.propertyName + " added link between nodes : " + myDiagram.findNodeForKey(e.newValue.from).data.id + " and " + myDiagram.findNodeForKey(e.newValue.to).data.id);
        } else if (e.change === go.ChangedEvent.Remove) {
            var deletedFromNode = deletedNodes.filter(function(item) {
                return item.key === e.oldValue.from;
            })[0];
            var deletedToNode = deletedNodes.filter(function(item) {
                return item.key === e.oldValue.to;
            })[0];

            console.log("tonode: " + deletedToNode);
            console.log("tonodekey: " + e.oldValue.to);
            console.log("fromnode: " + e.deletedFromNode);
            console.log("fromnodekey: " + e.oldValue.from);
            console.log(evt.propertyName + " removed link between nodes: " + deletedFromNode.id + " and " + deletedToNode.id);
        }
    }
});
});

However, at the point at the bottom of the code where it logs the from/to-nodes it’ll still say that the deleted node is undefined. I did deep clone/copy the object, as per this post: How to Deep Copy / Clone Data Structure in JavaScript

Example output from console:

CommittedTransaction removed node with id: d
tonode: [object Object]
tonodekey: 2
fromnode: undefined
fromnodekey: 5
TypeError: deletedFromNode is undefined

Edit: Nevermind: I’m being an idiot. Obviously both nodes haven’t been deleted. I will post working example in a bit.

Thanks for your help. Working example:

 myDiagram.addModelChangedListener(function(evt) {
    // ignore unimportant Transaction events
    if (!evt.isTransactionFinished) return;
    var txn = evt.object;  // a Transaction
    if (txn === null) return;

    var deletedNodes = new Map();

    // iterate over all of the actual ChangedEvents of the Transaction
    txn.changes.each(function(e) {
        // ignore any kind of change other than adding/removing a node
        if (e.modelChange === "nodeDataArray") {
            // record node insertions and removals
            if (e.change === go.ChangedEvent.Insert) {
                console.log(evt.propertyName + " added node with id: " + e.newValue.id);
            } else if (e.change === go.ChangedEvent.Remove) {
                console.log(evt.propertyName + " removed node with id: " + e.oldValue.id);

                deletedNodes.set(e.oldValue.key, e.oldValue);

                removeNode(e.oldValue);
            }
        }
    });

    txn.changes.each(function(e) {
        if (e.modelChange === "linkDataArray")
        {
            if (e.change === go.ChangedEvent.Insert) {
                console.log(evt.propertyName + " added link between nodes : " + myDiagram.findNodeForKey(e.newValue.from).data.id + " and " + myDiagram.findNodeForKey(e.newValue.to).data.id);
            } else if (e.change === go.ChangedEvent.Remove) {

                var fromNode = myDiagram.findNodeForKey(e.oldValue.from);
                var toNode = myDiagram.findNodeForKey(e.oldValue.to);

                var fromNodeData;
                var toNodeData;

                if (fromNode === null)
                {
                    fromNodeData = deletedNodes.get(e.oldValue.from);
                }
                else
                {
                    fromNodeData = fromNode.data;
                }
                if (toNode === null)
                {
                    toNodeData = deletedNodes.get(e.oldValue.to);
                }
                else
                {
                    toNodeData = toNode.data;
                }
                console.log(evt.propertyName + " removed link between nodes: " + fromNodeData.id + " and " + toNodeData.id);
                removeConnection(fromNodeData, toNodeData);
            }
        }
    });
});

Edit: deep copy was unnecessary.

It would be much more efficient to remember the deleted node data objects in a Map, where the key is the node key and the value is the data object, rather than in a List which you traverse each time when looking for a particular id.

Thank you for your suggestion. I edited the working copy above with a map, I just had to use the key as the key, not the id (because e.oldvalue.to/from are keys).

Quite right!