Undo Button in Ninox

By Adam Davidson, Director of Product Management

Sometimes it is useful to be able to undo an action in a database, which is not always possible to do in Ninox. Here is a way to put a functioning undo button into a Ninox table to be able to revert to the previous state easily.

First create a subtable in the desired table. This sub-table is where snapshots of the record’s state will be stored every time an update is made to the record. We will be allowing an unlimited number of undos but it may be smart to limit the number of undos per record if data is frequently modified in the table. Now we will create a Yes / No field named update (you will see why in a bit), and a button to click to undo the most recent action. In the subtable, we will create a single field named data, where the data for a record snapshot will be stored.

In this example we will be working with a simple table with three fields: Name, Email, and Phone. In the trigger after update function for each of these fields we will place the following code:

					if update then
	let state := formatJSON({
			Name: Name,
			Email: Email,
			Phone: Phone
	let xId := Id;
	let rec := (create 'undo Data');
	rec.(data := state);
	rec.(Undo := xId)
update := true


This code creates a JSON string containing all the fields in the table at the current point in time and creates a new record in the subtable (called “undo Data” in our case) and writes to the data field with the JSON string. This is only done when the update field is set to true to make sure that we can avoid creating a record when the undo button is clicked and modifies the fields. This is also why the function is placed in the trigger after update function for each of the fields rather than in the trigger after update for the whole record.

Now the following code will be placed in the on click function for the undo button:

					if cnt(undoData) > 1 then
	let data := parseJSON(item(undoData, cnt(undoData) - 2).data);
	update := false;
	Name := data.Name;
	Email := data.Email;
	Phone := data.Phone;
	delete item(undoData, cnt(undoData) - 2)


This code gets the most recent undo state from the subtable and sets the fields accordingly. Before it assigns the fields it changes the value of update to false so that the trigger after update function for the fields knows not to create a new record in the subtable.

The subtable and update fields should be hidden from view and now the undo functionality is ready to go. Next week we will take a look at how to build off of this to create a redo button to go alongside our undo button.