Undomanager with server-side IDs

Hello,

I got the following situation:

Every node/link/group that is created has a “serverId” stored in its data object. This serverId is required in order to update/create/delete nodes from the server. For example, if the serverId is null, I know that the node has to be created. If it is not null, I know it has to be updated. If I remove an element from the diagram (by hitting the “DEL” key or right click>context-menu>delete), I am checking the serverId. If it is null, I will only remove the element from the diagram. If it has a value, I know that I need to remove the node from the database by calling “deleteNode(serverId)”.

For performance reasons, the saving/updating/deleting happens only when the user hits the “save” button. This all works fine.

A problem arises when the user uses the undoManager. See the following example:

There is a Node which is already saved to the server, which means that it has a serverId. If I select the node and press “DEL”, it is removed from the diagram and added to the array of nodes to delete. If I hit “save” now, the “toDeleteNodes”-array is iterated and every node in this array is removed from the diagram. After that, the user does an undo (ctrl-z). The Node pops into existence one again and now has a serverId, although it has been deleted from the server. If I hit “save” now, my code will try to delete a non-existent object from the database, resulting in an error.

There is another problem too which I was not able to identify exactly, but it has the same nature as the bug when deleting a node, undoing, and deleting again. It happens when creating/updating nodes, saving, and using undo/redo excessively. I am certain that this bug also is solved with the following,but I do not know how to implement it:

As soon as the user saves, I create an array like this:

[
{
nodeId:1,
serverId:1234,
deleted:false
},
{
nodeId:3,
serverId:null,
deleted:true
},
...
]

After saving, I need to alter the undo-stack so that changes to the serverId will affect “all nodes that are in the undo-stack”. I put that in quotes because I do not quite understand how the undomanager handles transactions like this.

Something similar to this would be great:

saveNodes((success,nodeArray)=>{
   //NodeArray contains all Nodes, which have been 
   //created/updated/deleted
   modifyUndoStack(nodeArray);
   //All good now
})
modifyUndoStack = (nodeArray) =>{
   //Your magic
}

Thank you for your help in advance,
Martin

When you are setting data.serverId, are you just setting the property directly or are you calling Model.set (a.k.a. Model.setDataProperty)?

If updating the database is not supposed to happen after each undo or redo, then maybe you should not be recording changes to serverId in the UndoManager, so you should not be calling any model methods when changing serverId.

I am setting the ID directly. The ServerId should not be affected by the UndoManager, but rather be changed independently in the whole stack of the undomanager.

I managed to solve the problem with deleted Objects by creating a list of deleted ServerIds which is comapared to all nodes when saving, that way i can avoid deleting deleted objects.

I also found out what is causing the other bug:

Add Node
Save
Press Ctrl-Z
Save
Now, no element is in the diagram. However, delete was not called as i did not add the node to the “toDelete” list, which means when refreshing the browser, the element will be there again

When pressing ctrl-z, i need to extend “onUndo” (which i dont know how, please tell me) in order to add the element to the “toDelete” list. I also need to extend “onRedo” in order to revert the change. I looked already, but did not find any events that make this possible.

Basically, I need a list of nodes and what state they are changing to.

nevermind, I solved my issue. I was looking in “undoManager” but the onUndo and onRedo are located in “commandHandler”. With this I will probably be able to solve my issue. I will come back to this topic in case it does not work the way intended.

Although you can override those CommandHandler methods, it would be more general to implement a model Changed listener. Please see https://gojs.net/latest/intro/changedEvents.html#SavingModelWhenTransactionsComplete

Now that talks about saving the whole model at the end of any transaction or after any undo or redo. But clearly you would want to update your lists of added and removed parts so that the next “Save” will do the right thing.