How to set enabled state of an undo button

We are displaying an undo (and redo) button in our app. When there is no undo action, the button should be disabled.
We are using the following code to disabled/enable the undo button:

diagram.addModelChangedListener((e) => {
  if (e.isTransactionFinished) {
    button.disabled = !diagram.undoManager.canUndo();
  }
});

This doesn’t work sometimes, and it might be because the diagram.undoManager.history contains the “Initial Layout” event when the canUndo() method is called.

Is this approach supposed to work (and our application messes this up somewhere else) or should we use another approach?

GoJS Commands -- Northwoods Software
has an example that does exactly what I think you want.

Same issue in the sample you linked. Sometimes the undo button is enabled initially after page load:
grafik

This is sometimes reproducible for me when I force reload the page in Firefox (Ctrl + F5). It works as expected after a normal reload (F5) or with Chrome.
Our app is running in Electron and therefore Chromium though, so I don’t think it is related to a specific browser.

Not sure if it also depends on my internet connection speed or something else.

I normally use Firefox but do not encounter that problem even after repeated tries.

You’ll note that that example on that Introduction page includes a call to: setTimeout(enableAll, 1); I guess that’s not a reliable way to make sure the page has finished loading. Does increasing the delay reduce the problem for you?

And is the whole issue moot in your application?

Well, this issue means that the undo button is (sometimes) displayed as enabled in our app initially. And you can press it, but it has no effect. It’s not a major issue, but if I can fix it, I would do so :-)

setTimeout(5) seems to fix the issue, but what is a reasonable delay and how do I know it works for all users and not just on my system? Therefore I try to avoid using setTimeout if there are other ways to solve it.

If there is no other preferred way to solve this and it is normal that the “Initial Layout” event can appear (temporary) in the undo history, I’ll work with the setTimeout. Just didn’t want to make any assumptions which might backfire later.

I agree that using setTimeout isn’t ideal. However it is a reliable way to get something executed after the current running of JavaScript finishes. So maybe it would work reliably if in an “InitialLayoutCompleted” DiagramEvent listener you did something like setTimeout(enableAll, 1);.

At the time of that DiagramEvent everything should have been updated, but the transaction is still ongoing even though it’s basically finished (unless you make other changes then or in a “LayoutCompleted” DiagramEvent, and ignoring any animation). So calling setTimeout is one way of executing independent code after the transaction.

Okay, understood. Thanks!