Master / Detail
This section describes how to nest grids inside grids using a Master / Detail configuration.
With the Master Detail grid configuration, the top level grid is referred to as the 'master grid' and the nested grid
is referred to as the 'detail grid'.
With this configuration the detail grid can be used to display more information about the row in the master grid that was
expanded to reveal the detail grid.
Enabling Master Detail
To enable Master / Detail, you should set the following grid options:
-
masterDetail: Set to true to inform the grid you want to allow
expanding of rows to reveal detail grids.
-
detailGridOptions: The grid options to set for the detail grid.
The detail grid is a fully featured instance of ag-Grid, so any configuration
can be set on the detail grid that you would set any other grid.
-
getDetailRowData: A function you implement to provide the grid
with rows for display in the detail grids.
These grid options are illustrated below:
var masterGridOptions = {
columnDefs: masterColumnDefs,
rowData: rowData,
// enable master detail
masterDetail: true,
// specify params for default detail cell renderer
detailCellRendererParams: {
// provide detail grid options
detailGridOptions: detailGridOptions,
// extract and supply row data for detail
getDetailRowData: function(params) {
params.successCallback(params.data.childRecords);
}
}
}
var detailGridOptions = {
columnDefs: detailColumnDefs
}
Example - Simple Master / Detail
Below shows a simple Master / Detail setup. From the example you can notice the following:
- masterDetail - is set to
true in the master grid options.
- detailCellRendererParams - specifies the
detailGridOptions to use and getDetailRowData
extracts the data for the detail row.
Accessing Detail Grid API
You can access the API of all detail grids via the master grid. The API for each detail grid
is stored in a DetailGridInfo object that has the following properties:
interface DetailGridInfo {
// id of the detail grid, the format is detail_<row-id>
// where row-id is the id of the parent row.
id: string;
// the grid API of the detail grid
api: GridApi;
// the column API of the detail grid
columnApi: ColumnApi;
}
The DetailGridInfo is accessed via the GridApi of the master
gridOptions. You can either reference a particular detail grid API by ID,
or loop through all the existing detail grid APIs.
// lookup a specific DetailGridInfo by id, and then call stopEditing() on it
var detailGridInfo = masterGridOptions.api.getDetailGridInfo('someDetailGridId');
detailGridInfo.api.stopEditing();
// iterate over all DetailGridInfo's, and call stopEditing() on each one
masterGridOptions.api.forEachDetailGridInfo(function(detailGridInfo) {
console.log("detailGridInfo: ", detailGridInfo);
// then e.g. call stopEditing() on that detail grid
detailGridInfo.api.stopEditing();
});
The DetailGridInfo contains a reference to the underlying Grid API
and Column API for each detail grid. Methods invoked on these API's
will only operate on the specific detail grid.
Example - Editing Cells with Master / Detail
This example shows how to control cell editing when using Master / Detail. This examples demonstrates
the following:
- Edit Master - performs editing on a master cell using the master grid options:
masterGridOptions.api.startEditingCell()
- Stop Edit Master - iterates over each master row node using
masterGridOptions.api.forEachNode
and then calls masterGridOptions.api.stopEditing() on each node.
- Edit Detail - looks up the corresponding
DetailGridInfo using masterGridOptions.api.getDetailGridInfo()
and then uses the grid api on that detail grid start editing: detailGrid.api.startEditingCell()
- Stop Edit Detail - iterates over each detail grid using
masterGridOptions.api.forEachDetailGridInfo()
and then calls detailGridApi.api.stopEditing() on each detail grid.
Overriding the Default Detail Cell Renderer
The template used by default detail Cell Renderer can be overridden with a user defined template. This is a convenient
way to provide custom layouts and styles to the detail rows.
There are two ways to achieve this:
-
String Template - statically overrides the template used by the grid.
The same fixed template is used for each row.
-
Template Callback - called each time a detail row is shown so can
dynamically provide a template based on the data.
Both methods require specifying the detailCellRendererParams.template property as shown below:
// override using string template
detailCellRendererParams: {
template:
'<div style="background-color: #edf6ff; padding: 20px; box-sizing: border-box;">' +
' <div style="height: 10%;">Call Details</div>' +
' <div ref="eDetailGrid" style="height: 90%;"></div>' +
'</div>'
}
}
// override using template callback
detailCellRendererParams: {
template: function (params) {
var personName = params.data.name;
return '<div style="height: 100%; background-color: #EDF6FF; padding: 20px; box-sizing: border-box;">'
+ ' <div style="height: 10%;">Name: ' + personName + '</div>'
+ ' <div ref="eDetailGrid" style="height: 90%;"></div>'
+ '</div>';
}
}
The template must contain an element with attribute ref="eDetailGrid".
This element is used to host the detail grid instance.
The element containing ref="eDetailGrid" must be wrapped inside a div with the height set appropriately.
The following examples demonstrate both approaches.
Example - Customising via String Template
This examples demonstrates a static string template which is supplied to the detailCellRendererParams.template
property to customise the layout and background colour.
Example - Customising via Template Callback
A template callback function is supplied to the detailCellRendererParams.template property to customise
the layout and background colour. It additionally adds the name from the master row using the data supplied via callback parameters.
Providing a custom Detail Cell Renderer
The previous section described how to override the detail template used in the default Cell Renderer, however it is also
possible to provide a custom detail Cell Renderer Component.
This approach provides more flexibility but is more difficult to implement as you have to provide your
own custom detail cell renderer. Use this approach if you want to add additional functionality to the
detail panel that cannot be done by simply changing the template.
To supply a custom detail Cell Renderer instead of using the default use: gridOptions.detailCellRenderer
The following examples demonstrate custom Cell Renderer components for the detail row with and without a grid.
Example - Custom Detail Cell Renderer with a Grid
By the very nature of a custom detail cell renderer it can contain zero or many grid instances. For this reason if
you need the master grid to reference it's detail grids you will have to register them manually.
When the detail grid is initialised, register it via masterGridApi.addDetailGridInfo() like so:
onGridReady(params) {
var detailGridId = "detail_" + masterRowIndex;
var detailGridInfo = {
id: detailGridId,
api: params.api,
columnApi: params.columnApi
};
this.masterGridApi.addDetailGridInfo(detailGridId, detailGridInfo);
}
And in a similar way unregister it when the detail cell renderer is destroyed using:
var detailGridId = "detail_" + masterRowIndex;
this.masterGridApi.removeDetailGridInfo(detailGridId);
For details on how to access the detail grid api see:
Accessing Detail Grid API.
This example demonstrates how to embed a grid into the detail row using a custom Cell Renderer component:
Example - Custom Detail Cell Renderer with a Form
This example demonstrates a custom Cell Renderer Component that uses a form rather than a grid:
Configure Grid per Row
The property detailCellRendererParams can be a function to allow providing
different parameters for each row. This can be used to provide a differently configured grid
for each row.
The example below shows different grid configurations based on the data. Note the following:
-
Expanding rows 'Mila Smith' or 'Harper Johnson' will use a detail grid with the
columns {Call ID, Number}.
-
Expanding all other rows will use a detail grid with the columns {Call ID, Direction,
Duration, Switch Code}.
Dynamically Specify Master Nodes
It certain cases it may be required to not treat all top level rows as a master rows. For instance if a master has
no child records it may not be desirable to expand the master row to an empty detail.
In order to prevent this the following callback can be used on the master grid options:
masterGridOptions.isRowMaster = function (dataItem) {
// return true when row data has children, false otherwise
return dataItem ? dataItem.children.length > 0 : false;
}
As shown above our callback function will return true when there are detail (i.e. children) records,
otherwise it will return false.
Example - Dynamically Specify Master Nodes
The following example only shows detail rows when there are corresponding child records.
Nesting Master / Detail
It is possible to nest Master / Detail grids. There are no special configurations required to achieve this,
you just configure another detail grid inside the first detail grid.
The following snippet illustrates how to achieve nesting via successive grid option configurations:
// Level 1 (master)
var gridOptionsLevel1Master = {
...
masterDetail: true,
detailCellRendererParams: {
detailGridOptions: gridOptionsLevel2Master,
getDetailRowData: function (params) {
params.successCallback(params.data.children);
}
}
}
// Level 2 (detail and master)
var gridOptionsLevel2Master = {
...
masterDetail: true,
detailCellRendererParams: {
detailGridOptions: gridOptionsLevel3Detail,
getDetailRowData: function (params) {
params.successCallback(params.data.children);
}
}
}
// Level 3 (detail only)
var gridOptionsLevel3Detail = {
...
// no master / detail configuration
}
Example - Nesting Master / Detail
Below shows a contrived master detail setup to help illustrate how nesting can be achieved.
The example has very little data - this is on purpose to focus on the nesting.
Detail Row Height
The height of detail rows can be configured in one of the following two ways:
-
Use property
detailRowHeight to set a fixed height for each detail row.
-
Use callback
getRowHeight() to set height for each row individually.
One extra complication here is that this method is called for every row in the grid
including master rows.
The following snippet compares both approaches:
// option 1 - fixed detail row height, sets height for all details rows
masterGridOptions.detailRowHeight = 500;
// option 2 - dynamic detail row height, dynamically sets height for all rows
masterGridOptions.getRowHeight = function (params) {
var isDetailRow = params.node.detail;
if (isDetailRow) {
var detailPanelHeight = params.data.children.length * 50;
// dynamically calculate detail row height
return detailPanelHeight;
} else {
// for all non-detail rows, return 25, the default row height
return 25;
}
}
Note that the detail property can be used to identify detail rows.
The following examples demonstrate both approaches:
Example - Fixed Detail Row Height
The following demonstrates a fixed detail row height:
Example - Dynamic Detail Row Height
The following example demonstrates dynamic detail row heights:
Filtering and Sorting
There are no specific configurations for filtering and sorting with Master / Detail but as there are multiple grids
each grid will filter and sort independently.
Example - Filtering with Sort
Below shows a simple Master / Detail setup which has filtering and sorting enabled in both master and detail grids.
Lazy Load Detail Rows
It is possible to lazy load detail row data as it becomes available. For instance an asynchronous request could be
sent when expanding a master row to fetch detail records.
The following snippet illustrates this using a simple setTimeout() function to delay supplying
data to the detail row after a fixed timeout.
var masterGridOptions = {
detailCellRendererParams: {
getDetailRowData: function (params) {
// simulate delayed supply of data to the detail pane
setTimeout(function () {
params.successCallback(params.data.childRecords);
}, 1000);
}
}
};
Note that the key to this approach is the params.successCallback(data) function provided via the params, which can
be invoked later or asynchronously once the data for the detail row is available.
Example - Lazy Load Detail Rows
Below shows a simple Master / Detail setup which uses setTimeout() to simulate lazying loading of data
in the detail rows:
Supported Modes
The Master / Detail feature organises the grid in a way which overlaps with other features.
For example, Master / Detail expands rows, which is also the case with row grouping. For this reason,
Master / Detail does not work with certain grid configurations. These configurations are listed below:
Row Models
The master grid (i.e. the top level grid) in Master / Detail can be used in the
Client-side and
Server-side Row Models.
However it is not supported with the Viewport or
Infinite Row Models.
The detail grid (i.e. the child grid) can use any Row Model, as long as the
master grid uses the Client-side or
Server-side Row Models, then the detail
grid can use any of the other row models.
Tree Data
Master / Detail is not supported with Tree Data.
This is because the concept of tree data conflicts with Master / Detail, in that in tree
data, any row can expand to show child rows, which would result in a clash when a row
has child rows in addition to having Master / Detail at the same row.
Layouts
It is not possible to mix DOM layout
for master detail. This is because the layout is a CSS setting that would be inherited by all
grids contained with the master grid. So if your master grid was 'for-print', then all child grids
would pick up the 'for-print' layout.
When using Master / Detail and for-print,
then all detail grids need to use for-print.
When using Master / Detail and auto-height,
then all detail grids need to use auto-height.