Extending undo

Hi

We are currently using the undo manager in GoJS which works well. However we have a couple of issues due to the way our application is implemented

Our documents can contain multiple diagrams. We reuse the same GoJS instance for each diagram and we are losing the undo history when changing between different diagrams. I think this may be fixable by using addModel for each of our models. Is that right?

We’d also like to include other changes to our documents in undo. i.e we have text fields that are not related to the diagram. We also allow users to re-order the diagrams and other objects in our documents. Can we extend undo with our own changes or would we need to implement our own undo manager?

I’m not understanding the situation. Imagine the scenario where users modify model A, and then modify model B and then modify A again. Did you want the second editing of A to remember the undo history for the first editing of A but not remember any of the edits in model B?

If that’s the case, I recommend that you keep both models around, each with its own UndoManager. I’m assuming you have code like this:

    myDiagram = $(go.Diagram, "myDiagramDiv",
                  { . . .,
                    "undoManager.isEnabled": true
                  });

    myModel1 = new go.GraphLinksModel(...);
    myModel1.undoManager.isEnabled = true;

    myModel2 = new go.GraphLinksModel(...);
    myModel2.undoManager.isEnabled = true;
  }

  function exchangeModels() {
    if (myDiagram.model === myModel1) {
      myDiagram.model = myModel2;
    } else {
      myDiagram.model = myModel1;
    }
  }

And you have some command that calls exchangeModels() to swap between the two Models.

One solution is to do something like:

    myDiagram = $(go.Diagram, "myDiagramDiv",
                  { . . .,
                    "undoManager.isEnabled": true,

                    "ModelChanged": function(e) {
                      if (e.change === go.ChangedEvent.Transaction) {
                        // work-around for replacing Diagram.model not to always clear the model's UndoManager
                        // CAUTION: may change in future major version
                        if (e.propertyName === "CommittedTransaction" && e.oldValue === "Initial Layout") {
                          myDiagram.skipsUndoManager = true;
                          setTimeout(function() {
                            myDiagram.skipsUndoManager = false;
                          }, 1);
                        }
                      }
                    }
                  });

Yes, you can add as much non-GoJS state to UndoManagers as you like. Call Model.raiseDataChanged for simple property changes to JavaScript Objects, or Model.raiseChangedEvent for inserting or removing items from JavaScript Arrays.

OK, thanks. Regarding what we’d expect to happen with undo on different diagrams. If user changes A then B then A again, I guess I’d expect the undo to undo A then B then A. Currently we are just using the undo managers provided by default, so I think if we instead have a single undo manager this should give us what we want?

I noticed ChangedEvent has undo and redo methods. Is it possible to write my own ChangedEvent class and implement custom undo and redo for more complex situations?

Oh, yes, you can have a single UndoManager that is shared by multiple Models. Both the Seating Chart sample, Seating Chart, and the Two Diagrams sample, Two Diagrams, demonstrate the use of a single, shared UndoManager.

But they do so when two separate Diagrams are involved, which is different than in your case. And the default behavior is to clear the UndoManager state when replacing the Diagram.model. But you can avoid that by using the “ModelChanged” listener that I gave you, above.