Angular Data Grid

Undo / Redo Edits

angular logo

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 ... */>
</ag-grid-angular>

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:

Undo / Redo Events

The following events are relevant to undo / redo:

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:

Error: Line 6: Unexpected identifier

To troubleshoot paste snippet here: 'https://esprima.org/demo/parse.html'

const gridOptions = {
    defaultColDef: {
        // makes all cells editable
        editable: true
        // enables flashing to help see cell changes
        enableCellChangeFlash: true,
    },
    // allows copy / paste using cell ranges
    enableRangeSelection: true,

    // enables the fill handle
    enableFillHandle: true,

    // enables undo / redo
    undoRedoCellEditing: true,

    // restricts the number of undo / redo steps to 5
    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 ... */>
</ag-grid-angular>

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 ... */>
</ag-grid-angular>

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.