'Move' transaction is getting removed from undoManager.history, causing redo to not work

When I move a node, the undoManager.history is as shown in the first screenshot. After completing an undo (calling commandHandler.undo()) the history is as shown in the second screenshot containing no Move transactions, therefore Redo has no move to redo.

Screenshot 2023-02-09 at 4.11.32 PM
Screenshot 2023-02-09 at 4.11.56 PM

In doing some digging, I came across a post experiencing somewhat similar behavior and inserted this provided potential solution in my initDiagram() function:

// skips all events - prevents all events from being added to undoManager history
    diagram.model.undoManager.skipsEvent = function(e) {
      if (e !== null && e.diagram !== null) return true;
      return go.UndoManager.prototype.skipsEvent.call(this, e);
    };

    // commits a transaction for each move and updates target bindings, undo/redo works properly but affects other bindings
    diagram.model.addChangedListener(function(e) {
      if (e === null) return;
      setTimeout(function() {
        diagram.commit(function(d) {
          d.updateAllTargetBindings();
        }, null);
      }, 1);
    });

This has gotten me the closest to the desired behavior, undo and redo work properly but it messes up other functionalities related to other bindings. However, I have not been able to successfully specify the individual binding. The binding for the location of the nodes is as follows:

new Binding('location', 'nodeLocation', toPoint).makeTwoWay(fromPoint)

Some questions I have on potential solutions:

  1. Is there a way to modify the skipsEvent/addChangedListener to ignore the layout events in the undoManager so that, in this use case, only the Move events are recorded?
  2. Is there a way to modify the updateAllTargetBindings (or use an alternative) to only update the nodeLocation/location binding?

Thanks in advance for any help that can be provided!

That’s really suspicious. You normally never need to do anything to support undo and redo, as long as you are executing transactions around all modifications due to each user action or gesture.

This sample might be instructive: Update Demo GoJS Sample
Of course its implementation is a bit complicated because it needs to add state to the other components on the page that show a log of what is happening.

So my guess is that some change is happening unexpectedly, and that is causing the “Move” transaction (and any other transactions that you have undone) to disappear, as they should if the user decides to make a change.

The Changed listener that you have added is surprising – it’s causing binding updates to happen in a separate transaction. I’m not sure why anyone would think it’s needed, because normally all bindings are evaluated earlier on in the transaction.

I’ll do some more digging and look into that sample. For reference, this is where I found that changed listener: https://forum.nwoods.com/t/use-undomanager-for-virtualizedforcelayout/9745/8