Angular Data GridUndo / Redo Edits

This section covers how to allow users to undo / redo their cell edits.

When Cell Editing is enabled in the grid, it is usually desirable to allow users to undo / redo any edits.

Users can change the contents of cells through the following grid features:

This Undo / Redo feature is designed to be a recovery mechanism for user editing mistakes. Performing data updates (except for cell edits), or grid operations that change the row / column order, e.g. sorting, filtering and grouping, will clear the undo / redo stacks.

Enabling Undo / Redo

The following undo / redo properties are provided in the grid options interface:

<ag-grid-angular
    [undoRedoCellEditing]="undoRedoCellEditing"
    [undoRedoCellEditingLimit]="undoRedoCellEditingLimit"
    /* other grid options ... */ />

this.undoRedoCellEditing = true;
this.undoRedoCellEditingLimit = 20;

As shown in the snippet above, undo / redo is enabled through the undoRedoCellEditing property.

The default number of undo / redo steps is 10. To change this default the undoRedoCellEditingLimit property can be used.

Undo / Redo Shortcuts

The following keyboard shortcuts are available when undo / redo is enabled:

  • ^ Ctrl+Z / Command+Z: will undo the last cell edit(s).
  • ^ Ctrl+Y / Command+Y: will redo the last undo.

Note that the grid needs focus for these shortcuts to have an effect.

Undo / Redo API

It is also possible to programmatically control undo / redo and check the number of currently available undo / redo actions. These API methods are listed below:

undoCellEditing
Function
Reverts the last cell edit.
redoCellEditing
Function
Re-applies the most recently undone cell edit.
getCurrentUndoSize
Function
Returns current number of available cell edit undo operations.
getCurrentRedoSize
Function
Returns current number of available cell edit redo operations.

Undo / Redo Events

The following events are relevant to undo / redo:

cellValueChanged
CellValueChangedEvent
Value has changed after editing (this event will not fire if editing was cancelled, eg ESC was pressed) or if cell value has changed as a result of cut, paste, cell clear (pressing Delete key), fill handle, copy range down, undo and redo.
undoStarted
UndoStartedEvent
Undo operation has started.
undoEnded
UndoEndedEvent
Undo operation has ended.
redoStarted
RedoStartedEvent
Redo operation has started.
redoEnded
RedoEndedEvent
Redo operation has ended.

For an undo / redo, the events will be fired as:

  1. One undoStarted / redoStarted event.
  2. Zero to many cellValueChanged events.
  3. One undoEnded / redoEnded event.

When there are no undo / redo operations to perform, the started and ended events will still fire. However, the ended event will have a value of false for the operationPerformed property (compared to true when an operation was performed).

If the application is doing work each time it receives a cellValueChanged event, you can use the undoStarted / redoStarted and undoEnded / redoEnded events to suspend the application's work and then do the work for all cells impacted by the undo / redo operation afterwards.

If Read Only Edit is enabled, undo / redo will not perform any operations. The started and ended events will still fire, which means that you can implement your own undo / redo by keeping track of the cellEditRequest events.

Example: Undo / Redo

The example below has the following grid options enabled to demonstrate undo / redo:

<ag-grid-angular
    [defaultColDef]="defaultColDef"
    [cellSelection]="cellSelection"
    [undoRedoCellEditing]="undoRedoCellEditing"
    [undoRedoCellEditingLimit]="undoRedoCellEditingLimit"
    /* other grid options ... */ />

this.defaultColDef = {
    // makes all cells editable
    editable: true,
    // enables flashing to help see cell changes
    enableCellChangeFlash: true,
};

// allows copy / paste using cell ranges
this.cellSelection = {
    // enables the fill handle
    handle: {
        mode: 'fill',
    }
};

// enables undo / redo
this.undoRedoCellEditing = true;

// restricts the number of undo / redo steps to 5
this.undoRedoCellEditingLimit = 5;

To see undo / redo in action, try the following:

  • Cell Editing: click and edit some cell values.
  • Fill Handle: drag the fill handle to change a range of cells.
  • Copy / Paste: use ^ Ctrl+C / ^ Ctrl+V to copy and paste a range of cells.
  • Undo Shortcut: use ^ Ctrl+Z to undo the cell edits.
  • Redo Shortcut: use ^ Ctrl+Y to redo the undone cell edits.
  • Undo API: use the 'Undo' button to invoke gridApi.undoCellEditing().
  • Redo API: use the 'Redo' button to invoke gridApi.redoCellEditing().
  • Undo / Redo Limit: only 5 actions are allowed as undoRedoCellEditingLimit=5.

Complex Objects

If your cell values contain complex objects, there are a few steps necessary for undo / redo to work.

For manual editing, a Value Parser is required to convert string values back into complex objects.

<ag-grid-angular
    [columnDefs]="columnDefs"
    /* other grid options ... */ />

this.columnDefs = [
    {
        field: 'a',
        editable: true,
        valueParser: params => {
            // convert `params.newValue` string value into complex object
            return {
                actualValue: params.newValue,
                anotherProperty: params.data.anotherProperty,
            }
        }
    }
];

If a Value Getter is being used to create complex objects, a Value Setter must be used to update the data. colDef.equals is also needed when Comparing Values to determine if the cell value has changed for rendering.

<ag-grid-angular
    [columnDefs]="columnDefs"
    /* other grid options ... */ />

this.columnDefs = [
    {
        field: 'a',
        editable: true,
        valueGetter: params => {
            // create complex object from data
            return {
                actualValue: params.data[params.colDef.field],
                anotherProperty: params.data.anotherProperty,
            }
        },
        valueSetter: params => {
            // update data from complex object
            params.data[params.colDef.field] = params.newValue.actualValue
            return true
        },
        equals: (valueA, valueB) => {
            // compare complex objects
            return valueA.actualValue === valueB.actualValue
        }
    }
];

Complex object cell values must be immutable. If the cell values are mutated, undo / redo will not be able to restore the original values. This means that the Value Parser must return a new complex object.

Using the Cell Data Type object presets many of the grid features to allow complex objects to work without further configuration by leveraging the Value Formatter and Value Parser.

The following example demonstrates how to use complex objects with undo / redo.

  • For column A:
    • A Value Getter is used to create complex objects from the data.
    • A Value Formatter is used to convert the complex objects into strings for rendering.
    • A Value Setter is used to update the data from the complex objects (the inverse of the Value Getter).
    • A Value Parser is used to convert the string values produced from cell editing into complex objects (the inverse of the Value Formatter).
    • A Column Definition equals function is provided to compare the complex objects (without this the grid would use reference equality, but this won't work here as the Value Getter returns a new object each time).
  • For column B:
    • The column values are complex objects.
    • A Value Formatter is used to convert the complex objects into strings for rendering.
    • A Value Parser is used to convert the string values produced from cell editing into complex objects (the inverse of the Value Formatter).
  • For all columns:
    • The cell data type is set to object to allow other grid features to work, such as the fill handle, copy, paste, etc.
  • Try the following actions:
    • Cell Editing: click and edit some cell values.
    • Fill Handle: drag the fill handle to change a range of cells.
    • Copy / Paste: use ^ Ctrl+C / ^ Ctrl+V to copy and paste a range of cells.
    • Undo Shortcut: use ^ Ctrl+Z to undo the cell edits.
    • Redo Shortcut: use ^ Ctrl+Y to redo the undone cell edits.
    • Undo API: use the 'Undo' button to invoke gridApi.undoCellEditing().
    • Redo API: use the 'Redo' button to invoke gridApi.redoCellEditing().
    • Undo / Redo Limit: only 5 actions are allowed as undoRedoCellEditingLimit=5.