I am thinking about using Handsontable (https://github.com/handsontable/handsontable) on my project.
I need to have multiple table components on the page,
and have two own buttons for undo/redo which handles undo/redo action over all components in same order as user did changes.
I mean it like user made change in table A then in table B and then again in table A. Then if he hits undo, then A is changed, after next hit B is changed and after next hit A is changed back.
Is it somehow possible to do that?
So that we can close this question and for people who would like to get advice on how to do this, here is a short idea of how to implement this feature:
Assumptions:
undo
and redo
methods, defined below as per the Handsontable documentation.id
s #undo
and #redo
to trigger the
behavior.Methods:
undo (): Undo last edit
redo (): Redo edit (used to reverse an undo)
The technique is simple. We will be using two global arrays, abstract Queue
s, defined as undoQ
and redoQ
which will keep track of the reference of the last HOT instance to be modified. These Queue
s are modified every time the afterChange
event is fired on any of the HOT instances, when the redo
button is pressed, and when the undo
button is pressed. To avoid over-complicating this approach, we will not be optimizing for space, but I will make a note of it so that if you need it, you can attempt to do it yourself.
Disclaimer: you did not mention what would happen when one or the other table is in focus. The way HOT works is that ctrl-z
, if enabled, will undo
whichever table is in focus. You can disable this if you'd like.
Step 1: Add event logic
Add the afterChange
event hook as follows, or append the logic if you already have a custom event; this goes in the declaration of the options
of the HOT instance.
afterChange: function() {
return function(changes, source) {
var hotInstance = this;
// check that the `undo` event was initialized before pushing
if (hotInstance.undo) {
undoQ.push(this);
redoQ = []; // IMPORTANT: this clears the redo array as do most undo operations, but you can add different logic if you'd like
}
// other logic if you have some
}
}
This will push the REFERENCE to each table as a change occurs. Note that this array could get big but not terribly so. The reason is that the afterChange
event is triggered only once per change
. However, here is where the optimization can happen. What you could do instead, if you notice that you are triggering this event an absurd amount of times, is check if the last element in the Queues is the same as this
, and if so, increase a counter rather than just append the reference. Then on undo
or redo
, you decrease the counters until they reach zero, and then pop.
Step 2: Add undo and redo logic
This step is a little more intuitive. What you want to do is add the following logic to the callback of the undo
and redo
button clicks.
$("#undo").on("click", function() {
if (undoQ.length > 0) {
var hotInstance = undoQ.shift();
hotInstance.undo();
redoQ.push(hotInstance);
}
})
$("#redo").on("click", function() {
if (redoQ.length > 0) {
var hotInstance = redoQ.shift();
hotInstance.redo();
undoQ.push(hotInstance);
}
})
What we are doing here is manually triggering the respective events of whichever table was last changed. Remember that after a change, redoQ
is emptied but as long as there aren't any, you should be able to undo and redo with no problem.
That should be it. Good luck!