React Grid: Cell Editors
Create your own cell editor by providing a cell editor component.
Simple Cell Renderer
Below is a simple example of cell renderer as a Hook:
const DoublingEditor = forwardRef((props, ref) => {
const [value, setValue] = useState(parseInt(props.value));
const refInput = useRef(null);
useEffect(() => {
// focus on the input
setTimeout(() => refInput.current.focus());
}, []);
/* Component Editor Lifecycle methods */
useImperativeHandle(ref, () => {
return {
// the final value to send to the grid, on completion of editing
getValue() {
// this simple editor doubles any value entered into the input
return value * 2;
},
// Gets called once before editing starts, to give editor a chance to
// cancel the editing before it even starts.
isCancelBeforeStart() {
return false;
},
// Gets called once when editing is finished (eg if enter is pressed).
// If you return true, then the result of the edit will be ignored.
isCancelAfterEnd() {
// our editor will reject any value greater than 1000
return value > 1000;
}
};
});
return (
<input type="number"
ref={refInput}
value={value}
onChange={event => setValue(event.target.value)}
style={{width: "100%"}}
/>
);
});
And here is the same example as a Class-based Component:
export default class DoublingEditor extends Component {
constructor(props) {
super(props);
this.inputRef = createRef();
this.state = {
value: parseInt(props.value)
};
}
componentDidMount() {
setTimeout(() => this.inputRef.current.focus());
}
/* Component Editor Lifecycle methods */
// the final value to send to the grid, on completion of editing
getValue() {
// this simple editor doubles any value entered into the input
return this.state.value * 2;
}
// Gets called once before editing starts, to give editor a chance to
// cancel the editing before it even starts.
isCancelBeforeStart() {
return false;
}
// Gets called once when editing is finished (eg if enter is pressed).
// If you return true, then the result of the edit will be ignored.
isCancelAfterEnd() {
// our editor will reject any value greater than 1000
return this.state.value > 1000;
}
render() {
return (
<input ref={this.inputRef}
value={this.state.value}
onChange={event => this.setState({value: event.target.value})}
style={{width: "100%"}}
/>
);
}
}
Simple Cell Editor Example
The example below shows a few simple cell editors in action.
- The
Doubling
Cell Editor will double a given input and reject values over a 1000 - The
Mood
Cell Editor illustrates a slightly more complicated editor with values changed depending on the smiley chosen - The
Numeric
Cell Editor illustrates a slightly more complicated numeric editor to theDoubling
editor above, with increased input validation and better initial carot behaviour
Cell Editor Component
When a React component is instantiated the grid will make the grid APIs, a number of utility methods as well as the cell &
row values available to you via props
.
The editor interface is as follows:
interface {
// Should return the final value to the grid, the result of the editing
getValue(): any;
// Gets called once after initialised.
// If you return true, the editor will appear in a popup
isPopup?(): boolean;
// Gets called once, only if isPopup() returns true. Return "over" if the
// popup should cover the cell, or "under" if it should be positioned below
// leaving the cell value visible. If this method is not present, the
// default is "over"
getPopupPosition?(): string;
// Gets called once before editing starts, to give editor a chance to
// cancel the editing before it even starts.
isCancelBeforeStart?(): boolean;
// Gets called once when editing is finished (eg if enter is pressed).
// If you return true, then the result of the edit will be ignored.
isCancelAfterEnd?(): boolean;
// If doing full row edit, then gets called when tabbing into the cell.
focusIn?(): boolean;
// If doing full row edit, then gets called when tabbing out of the cell.
focusOut?(): boolean;
}
Note that if you're using Hooks for Grid Components that have lifecycle/callbacks that the
grid will call (for example, the getValue
callback from an Editor Component), then you'll need to expose them with
forwardRef
& useImperativeHandle
.
Please refer to the Hook documentation (or the examples on this page) for more information.
The interface for values available on on component creation (via props
) is as follows:
interface ICellEditorParams {
// current value of the cell
value: any;
// key code of key that started the edit, eg 'Enter' or 'Delete' - non-printable characters appear here
keyPress: number;
// the string that started the edit, eg 'a' if letter a was pressed, or 'A' if shift + letter a
// - only printable characters appear here
charPress: string;
// grid column
column: Column;
// grid row node
node: RowNode;
// editing row index
rowIndex: number,
// grid API
api: GridApi;
// column API
columnApi: ColumnApi;
// If doing full row edit, this is true if the cell is the one that started the edit (eg it is the cell the
// use double clicked on, or pressed a key on etc).
cellStartedEdit: boolean;
// the grid's context object
context: any;
// angular 1 scope - null if not using angular 1, this is legacy and not used if not using angular 1
$scope: any;
// callback to tell grid a key was pressed - useful to pass control key events (tab, arrows etc)
// back to grid - however you do
onKeyDown: (event: KeyboardEvent)=>void;
// Callback to tell grid to stop editing the current cell. pass 'false' to prevent navigation moving
// to the next cell if grid property enterMovesDownAfterEdit=true
stopEditing: (suppressNavigateAfterEdit?: boolean)=>void;
// A reference to the DOM element representing the grid cell that your component will live inside. Useful if you
// want to add event listeners or classes at this level. This is the DOM element that gets browser focus when selecting cells.
eGridCell: HTMLElement;
// Utility function to parse a value using the column's colDef.valueParser
parseValue: (value: any) => any;
// Utility function to format a value using the column's colDef.valueFormatter
formatValue: (value: any) => any;
}
Registering Cell Editors with Columns
See the section registering custom components for details on registering and using custom cell editors.
Complementing Cell Editor Params
As with cell renderers, cell editors can also be provided with additional parameters. Do this using cellEditorParams
as in the following example which will pass 'Ireland' as the 'country' parameter:
// define cell editor to be used
const MyCellEditor = ...cell editor definition...
const GridExample = () => {
// other properties & methods
frameworkComponents = {
'myCellEditor': MyCellEditor
};
return (
<div className="ag-theme-alpine">
<AgGridReact
frameworkComponents={frameworkComponents}
...other properties>
{ /* make "country" value available to cell editor */ }
<AgGridColumn headerName="Value Column" field="value" cellEditor="myCellEditor" cellEditorParams={{ country: 'Ireland' }} />
</AgGridReact>
</div>
);
};
Keyboard Navigation While Editing
If you provide a cell editor, you may wish to disable some of the grids keyboard navigation. For example, if you are providing a simple text editor, you may wish the grid to do nothing when you press the right and left arrows (the default is the grid will move to the next / previous cell) as you may want the right and left arrows to move the cursor inside your editor. In other cell editors, you may wish the grid to behave as normal.
Because different cell editors will have different requirements on what the grid does, it is up to the cell editor to decide which event it wants the grid to handle and which it does not.
You have two options to stop the grid from doing it's default action on certain key events:
- Stop propagation of the event to the grid in the cell editor.
- Tell the grid to do nothing via the
colDef.suppressKeyEvent()
callback.
Option 1 - Stop Propagation
If you don't want the grid to act on an event, call event.stopPropagation()
. The advantage of this method is that your cell editor takes care of everything, good for creating reusable cell editors.
The follow code snippet is one you could include for a simple text editor, which would stop the grid from doing navigation.
const KEY_LEFT = 37;
const KEY_UP = 38;
const KEY_RIGHT = 39;
const KEY_DOWN = 40;
const KEY_PAGE_UP = 33;
const KEY_PAGE_DOWN = 34;
const KEY_PAGE_HOME = 36;
const KEY_PAGE_END = 35;
const MyCellEditor = forwardRef((props, ref) => {
const [value, setValue] = useState(props.value);
/* Component Editor Lifecycle methods */
useImperativeHandle(ref, () => {
return {
getValue() {
return value;
}
};
});
onKeyDown(event) {
const keyCode = event.keyCode;
const isNavigationKey = keyCode === KEY_LEFT ||
keyCode === KEY_RIGHT ||
keyCode === KEY_UP ||
keyCode === KEY_DOWN ||
keyCode === KEY_PAGE_DOWN ||
keyCode === KEY_PAGE_UP ||
keyCode === KEY_PAGE_HOME ||
keyCode === KEY_PAGE_END;
if (isNavigationKey) {
// this stops the grid from receiving the event and executing keyboard navigation
event.stopPropagation();
}
}
return (
<input value={value}
onKeyDown={event => onKeyDown(event)}
/>
);
});
Option 2 - Suppress Keyboard Event
If you implement colDef.suppressKeyboardEvent()
, you can tell the grid which events you want process and which not. The advantage of this method of the previous method is it takes the responsibility out of the cell editor and into the column definition. So if you are using a reusable, or third party, cell editor, and the editor doesn't have this logic in it, you can add the logic via configuration.
const KEY_UP = 38;
const KEY_DOWN = 40;
const GridExample = () => {
// rest of the component
return (
<div
style={{
height: '100%',
width: '100%'
}}
className="ag-theme-alpine test-grid">
<AgGridReact ...rest of the definition...>
<AgGridColumn field="value"
suppressKeyboardEvent={params => {
console.log('cell is editing: ' + params.editing);
console.log('keyboard event:', params.event);
// return true (to suppress) if editing and user hit up/down keys
const keyCode = params.event.keyCode;
const gridShouldDoNothing = params.editing && (keyCode===KEY_UP || keyCode===KEY_DOWN);
return gridShouldDoNothing;
}}
/>
</AgGridReact>
</div>
);
};
|```
The params for suppressKeyboardEvent( )
are as follows:
interface SuppressKeyboardEventParams {
// the keyboard event the grid received
event: KeyboardEvent;
// whether the cell is editing or not
editing: boolean;
// these are same as normal
node: RowNode;
column: Column;
colDef: ColDef;
context: any;
api: GridApi;
columnApi: Co lumnApi;
}
Cell Editing Example
The example below illustrates:
- 'Gender' column uses a Component cell editor that allows choices via a 'richSelect' (AG Grid Enterprise only), with values supplied by complementing the editor parameters.
- 'Age' column uses a Component cell editor that allows simple integer input only.
- 'Mood' column uses a custom Component cell editor and renderer that allows choice of mood based on image selection.
- 'Address' column uses a Component cell editor that allows input of multiline text via a 'largeText'. Tab & Esc (amongst others) will exit editing in this field, Shift+Enter will allow newlines.
- 'Country' columns shows using 'richSelect' for a complex object - the cell renderer takes care of only rendering the country name.
Accessing Cell Editor Instances
After the grid has created an instance of a cell editor for a cell it is possible to access that instance. This is useful if you want to call a method that you provide on the cell editor that has nothing to do with the operation of the grid. Accessing cell editors is done using the grid API getCellEditorInstances(params)
.
// function takes params to identify what cells and returns back a list of cell editors
function getCellEditorInstances(params: GetCellEditorInstancesParams): ICellRendererComp[];
// params object for the above
interface GetCellEditorInstancesParams {
// an optional list of row nodes
rowNodes?: RowNode[];
// an optional list of columns
columns?: (string|Column)[];
}
If you are doing normal editing, then only one cell is editable at any given time. For this reason if you call getCellEditorInstances()
with no params, it will return back the editing cell's editor if a cell is editing, or an empty list if no cell is editing.
An example of calling getCellEditorInstances()
is as follows:
const instances = gridOptions.api.getCellEditorInstances(params);
if (instances.length > 0) {
const instance = instances[0];
}
If your editor is a component you can get the underlying cell editor using getFrameworkComponentInstance()
method on the grid API:
// example - get cell editor
const instances = this.api.getCellEditorInstances(params);
if (instances.length > 0) {
// got it, user must be scrolled so that it exists
const wrapperInstance = instances[0];
// non-popup editor instance
const frameworkInstance = wrapperInstance.getFrameworkComponentInstance();
// popup editor instance
const frameworkInstance = wrapperInstance.cellEditor.getFrameworkComponentInstance();
}
The example below shows using getCellEditorInstances
. The following can be noted:
- All cells are editable.
- First Name and Last Name use the default editor.
- All other columns use the provided
MySimpleCellEditor
editor. - The example sets an interval to print information from the active cell editor. There are three results: 1) No editing 2) Editing with default cell renderer and 3) editing with the custom cell editor. All results are printed to the developer console.
- Cell Editors
- Simple Cell Renderer
- Simple Cell Editor Example
- Cell Renderer Component
- Cell Editor Component
- Cell Editor Component
- Cell Renderer Component
- Registering Cell Editors with Columns
- Complementing Cell Editor Params
- Keyboard Navigation While Editing
- Option 1 - Stop Propagation
- Option 2 - Suppress Keyboard Event
- Cell Editing Example
- Accessing Cell Editor Instances