JavaScript Data GridCell Renderer
By default the grid will create the cell values using simple text. If you want more complex HTML inside the cells you can achieve this using Cell Renderers.
Below is a simple example of cell renderer class:
class TotalValueRenderer {
// gets called once before the renderer is used
init(params) {
// create the cell
this.eGui = document.createElement('div');
this.eGui.innerHTML = `
<span>
<span class="my-value"></span>
<button class="btn-simple">Push For Total</button> </span>`;
// get references to the elements we want
this.eButton = this.eGui.querySelector('.btn-simple');
this.eValue = this.eGui.querySelector('.my-value');
// set value into cell
this.cellValue = this.getValueToDisplay(params);
this.eValue.innerHTML = this.cellValue;
// add event listener to button
this.eventListener = () => alert(`${this.cellValue} medals won!`);
this.eButton.addEventListener('click', this.eventListener);
}
getGui() {
return this.eGui;
}
// gets called whenever the cell refreshes
refresh(params) {
// set value into cell again
this.cellValue = this.getValueToDisplay(params);
this.eValue.innerHTML = this.cellValue;
// return true to tell the grid we refreshed successfully
return true;
}
// gets called when the cell is removed from the grid
destroy() {
// do cleanup, remove event listener from button
if (this.eButton) {
// check that the button element exists as destroy() can be called before getGui()
this.eButton.removeEventListener('click', this.eventListener);
}
}
getValueToDisplay(params) {
return params.valueFormatted ? params.valueFormatted : params.value;
}
}
The example below shows a simple Cell Renderer in action. It uses a Cell Renderer to render a hash (#
) symbol for each medal won
(instead of the medal count), and the MedalCellRenderer
defined in the code snippet above for the Total
column:
The interface for the cell renderer component is as follows:
interface ICellRendererComp {
// Optional - Params for rendering. The same params that are passed to the cellRenderer function.
init?(params: ICellRendererParams): void;
// Mandatory - Return the DOM element of the component, this is what the grid puts into the cell
getGui(): HTMLElement;
// Optional - Gets called once by grid after rendering is finished - if your renderer needs to do any cleanup,
// do it here
destroy?(): void;
// Mandatory - Get the cell to refresh. Return true if the refresh succeeded, otherwise return false.
// If you return false, the grid will remove the component from the DOM and create
// a new component in its place with the new values.
refresh(params: ICellRendererParams): boolean;
}
The interface for the cell renderer parameters is as follows:
ICellRendererParams
Properties available on the ICellRendererParams<TData = any, TValue = any, TContext = any>
interface.
| Value to be rendered. | |
| Formatted value to be rendered. | |
| True if this is a full width row. | |
| Pinned state of the cell. | |
| The row's data. Data property can be undefined when row grouping or loading infinite row models. | |
| The row node. | |
| The current index of the row (this changes after filter and sort). | |
| The cell's column definition. | |
| The cell's column. | |
| The grid's cell, a DOM div element. | |
| The parent DOM item for the cell renderer, same as eGridCell unless using checkbox selection. | |
| Convenience function to get most recent up to date value.
| |
| Convenience function to set the value.
| |
| Convenience function to format a value using the column's formatter.
| |
| Convenience function to refresh the cell.
| |
| registerRowDragger:
rowDraggerElement The HTMLElement to be used as Row Dragger
dragStartPixels The amount of pixels required to start the drag (Default: 4)
value The value to be displayed while dragging. Note: Only relevant with Full Width Rows.
suppressVisibilityChange Set to true to prevent the Grid from hiding the Row Dragger when it is disabled.
| |
| The grid api. | |
| The column api. | |
| Application context as set on gridOptions.context . |
See the section registering custom components for details on registering and using custom Cell Renderers.
Component Refresh needs a bit more explanation. Here we go through some of the finer details.
The grid can refresh the data in the browser, but not every refresh / redraw of the grid results in the refresh method of your cell renderer getting called. The following items are those that do cause refresh to be called:
- Calling
rowNode.setDataValue(colKey, value)
to set a value directly onto therowNode
. This is the preferred API way to change one value from outside of the grid. - When editing a cell and editing is stopped, so that cell displays new value after editing.
- Calling
api.refreshCells()
to inform grid data has changed (see Refresh).
If any of the above occur and the grid confirms the data has changed via Change Detection, then the refresh()
method will be called.
The following will not result in the cell renderer's refresh method being called:
- Calling
rowNode.setData(data)
to set new data into arowNode
. When you set the data for the whole row, the whole row in the DOM is recreated again from scratch. - Scrolling the grid vertically causes columns (and their containing cells) to be removed and inserted due to column virtualisation.
All of the above will result in the component being destroyed and recreated.
The refresh method returns back a boolean value. If you do not want to handle the refresh in the cell renderer, just return back false
from an otherwise empty method. This will indicate to the grid that you did not refresh and the grid will instead destroy the component and create another instance of your component from scratch instead.
As mentioned in the section on Change Detection, the refresh of the Cell will not take place if the value getting rendered has not changed.
The lifecycle of the cell renderer is as follows:
new
is called on the class.init()
is called once.getGui()
is called 0 or 1 times (destroy
could get called first, i.e. when scrolling quickly)refresh()
is called 0...n times (i.e. it may never be called, or called multiple times)destroy()
is called once.
In other words, new()
, init()
and destroy()
are always called exactly once. getGui()
will typically get called once unless destroy()
is called first. refresh()
is optionally called multiple times.
When implementing destroy()
it is important to check that any elements created in getGui()
exist, as when scrolling quickly destroy()
can get called first. Calling getGui()
unnecessarily would negatively affect scroll performance.
If you are doing refresh()
, remember that getGui()
is only called once (assuming the cell renderer hasn't been destroyed first), so be sure to update the existing GUI in your refresh, do not think that the grid is going to call getGui()
again to get a new version of the GUI.
The diagram below (which is taken from the section Cell Content) summarises the steps the grid takes while working out what to render and how to render.
In short, a value is prepared. The value comes using either the colDef.field
or the colDef.valueGetter
. The value is also optionally passed through a colDef.valueFormatter
if it exists. Then the value is finally placed into the DOM, either directly, or by using the chosen colDef.cellRenderer
.
On top of the parameters provided by the grid, you can also provide your own parameters. This is useful if you want to 'configure' your Cell Renderer. For example, you might have a Cell Renderer for formatting currency but you need to provide what currency for your cell renderer to use.
Provide params to a cell renderer using the colDef option cellRendererParams
.
// define cellRenderer to be reused
const myCellRenderer = params => `<span style="color:${params.color}">${params.value}</span>`;
// use with a colour
colDef.cellRenderer = myCellRenderer;
colDef.cellRendererParams = {
color: 'guinnessBlack'
}
// use with another colour
colDef.cellRenderer = myCellRenderer;
colDef.cellRendererParams = {
color: 'irishGreen'
}
Sometimes the data
property in the parameters given to a cell renderer might not be populated. This can happen for
example when using row grouping (where the row node has aggData
and groupData
instead of data
), or when rows are
being loaded in the Infinite Row Model and do not yet have data. It is best to check that data
does exist before accessing it in your cell renderer, for example:
colDef.cellRenderer = params => {
// check the data exists, to avoid error
if (params.data) {
// data exists, so we can access it
return `**${params.data.theBoldValue}**`;
}
// when we return null, the grid will display a blank cell
return null;
};
Instead of using a component, it's possible to use a simple function for a cell renderer. The function takes the same parameters as the cell renderer init
method in the component variant. The function should return back either a) a string of HTML or b) a DOM object.
Use the function variant of a cell renderer if you have no refresh or cleanup requirements (ie you don't need to implement the refresh or destroy functions).
Below are some simple examples of cell renderers provided as simple functions:
// put the value in bold
colDef.cellRenderer = params => `**${params.value.toUpperCase()}**`;
// put a tooltip on the value
colDef.cellRenderer = params => `<span title="the tooltip">${params.value}</span>`;
// create a DOM object
colDef.cellRenderer = params => {
const eDiv = document.createElement('div');
eDiv.innerHTML = '<span class="my-css-class"><button class="btn-simple">Push Me</button></span>';
const eButton = eDiv.querySelectorAll('.btn-simple')[0];
return eDiv;
}
You might be wondering how the grid knows if you have provided a Cell Renderer component class or a simple function, as JavaScript uses functions to implement classes. The answer is the grid looks for the getGui() method in the prototype of the function (a mandatory method in the cell renderer interface). If the getGui() method exists, it assumes a component, otherwise it assumes a function.
The example below shows five columns formatted, demonstrating each of the methods above.
- 'Month' column uses
cellStyle
to format each cell in the column with the same style. - 'Max Temp' and 'Min Temp' columns uses the Function method to format each cell in the column with the same style.
- 'Days of Air Frost' column uses the Component method to format each cell in the column with the same style
- 'Days Sunshine' and 'Rainfall (10mm)' use simple functions to display icons.
The example below demonstrates how to implement a simple custom group cell renderer.
- The example has a custom icon which represents whether the group is open
- Reacts to the row events if the group is expanded from another source
- Cleans up event listeners when it's disposed of
After the grid has created an instance of a cell renderer 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 renderer that has nothing to do with the operation of the grid. Accessing cell renderers is done using the grid API getCellRendererInstances(params)
.
| Returns the list of active cell renderer instances.
|
An example of getting the cell renderer for exactly one cell is as follows:
// example - get cell renderer for first row and column 'gold'
const firstRowNode = gridOptions.api.getDisplayedRowAtIndex(0);
const params = { columns: ['gold'], rowNodes: [firstRowNode] };
const instances = gridOptions.api.getCellRendererInstances(params);
if (instances.length > 0) {
// got it, user must be scrolled so that it exists
const instance = instances[0];
}
Note that this method will only return instances of the cell renderer that exists. Due to row and column virtualisation, renderers will only exist for cells that the user can actually see due to horizontal and vertical scrolling.
The example below demonstrates custom methods on cell renderers called by the application. The following can be noted:
- The medal columns are all using the user defined
MedalCellRenderer
. The cell renderer has an arbitrary methodmedalUserFunction()
which prints some data to the console. - The Gold method executes a method on all instances of the cell renderer in the gold column.
- The First Row Gold method executes a method on the gold cell of the first row only. Note that the
getCellRendererInstances()
method will return nothing if the grid is scrolled far past the first row showing row virtualisation in action. - The All Cells method executes a method on all instances of all cell renderers.
When using custom cell renderers, the custom cell renderer is responsible for implementing support for keyboard navigation among its focusable elements. This is why by default, focusing a grid cell with a custom cell renderer will focus the entire cell instead of any of the elements inside the custom cell renderer.
Adding support for keyboard navigation and focus requires a custom suppressKeyboardEvent
function in grid options. See Suppress Keyboard Events.
An example of this is shown below, enabling keyboard navigation through the custom cell elements when pressing Tab and Shift+Tab:
- Click on the top left
Natalie Coughlin
cell, press the Tab key and notice that the button, textbox and link can be tabbed into. At the end of the cell elements, the tab focus moves to the next cell in the next row - Use Shift+Tab to navigate in the reverse direction
The suppressKeyboardEvent
callback is used to capture tab events and determine if the user is tabbing forward or backwards. It also suppresses the default behaviour of moving to the next cell if tabbing within the child elements.
If the focus is at the beginning or the end of the cell children and moving out of the cell, the keyboard event is not suppressed, so focus can move between the children elements. Also, when moving backwards, the focus needs to be manually set while preventing the default behaviour of the keyboard press event.