The JavaScript API can be used to group several steps into one undo action that can be undone with a single undo call. Additionally, those groups are visually updated at once to avoid screen flickering when updating larger portions of a document.

Without Grouping

In the following sample, a table should be added to the document and each cell is filled programmatically in a loop. The following code can be used to achieve this:

TXTextControl.tables.add(5, 5, 10, function(e) {
if (e === true) { // if added
TXTextControl.tables.getItem(function(table) {
table.cells.forEach(function(cell) {
cell.setText("Cell Text");
});
}, null, 10);
}
})
view raw test.js hosted with ❤ by GitHub

Basically, a new table is added and for each table cell in that table, the cell's text is set. That works perfectly fine, but there are 2 major drawbacks in this solution:

  • The user is able to see the visual updates of the document manipulation.
  • In order to remove this table, 26 undo steps are required (25 cells + table insertion).

Table insertion

With Grouping

In order to group undo steps, the methods beginUndoAction and endUndoAction can be used. All calls between these two method calls are grouped as one undo step and visually updated together.

To do this, the above code must be slightly changed by using asynchronous promises to wait until all cells have been completed before calling the endUndoAction.

function insertTableWithText() {
// start the undo action (open group)
TXTextControl.beginUndoAction("Table insertion");
// add a table
TXTextControl.tables.add(5, 5, 10, function(e) {
if (e === true) { // if added
TXTextControl.tables.getItem(async function(table) {
console.log("setting cell text...");
// async setting of cell text
await setCellText(table);
console.log("setting cell text done.");
// stop the undo action (close group)
TXTextControl.endUndoAction();
}, null, 10);
}
})
}
function setCellText(table) {
return new Promise(resolve => {
table.cells.getCount(function(count) {
// loop through all table cells
for (let i = 0; i < count; i++) {
table.cells.elementAt(i, function(cell) {
console.log("setting text for cell " + i);
cell.setText("Cell " + i);
// resolve on last cell
if (i == count - 1) {
resolve();
}
});
}
});
});
}
view raw test.js hosted with ❤ by GitHub

Table insertion

Conclusion

Undo grouping should be used for all calls where larger parts of text or content are modified within the document. This concept enables users to restore previous states with a single undo step and visual updates are rendered in one step without showing the each single call visually.