Master / Detail

This section describes how to nest grids inside grids using a Master / Detail configuration.

Master / Detail Video Tutorial

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(; } } } 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.

Syncing Detail scrolling with Master

By default, detail grids scroll independently to the master grid. This happens because the Master Detail is rendered inside the Full Width Row (an element that spans across the Grid Viewport).

To force this full width row to fill the Grid scrollable area, it is necessary to enable the embedFullWidthRows property.

Example: Details scrolls horizontally with Master

In the example below, notice that horizontal scrolling from within the detail grid also scrolls the master:

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 =; 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(; } } } // Level 2 (detail and master) var gridOptionsLevel2Master = { ... masterDetail: true, detailCellRendererParams: { detailGridOptions: gridOptionsLevel3Detail, getDetailRowData: function (params) { params.successCallback(; } } } // 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:

  1. Use property detailRowHeight to set a fixed height for each detail row.
  2. 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 = * 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(; }, 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:

Keeping Detail Rows

When a master row is expanded a detail row is created. When the master row is collapsed, the detail row is destroyed. When the master row is expanded a second time, a detail rows is created again from scratch. This can be undesirable behaviour if there was context in the detail row, e.g. if the user sorted or filtered data in the detail row, the sort or filter will be reset the second time the detail row is displayed.

To prevent losing the context of details rows, the grid provides two properties to cache the details rows to be reused the next time the row is shows. The properties are as follows:

keepDetailRowsSet to true to keep detail rows for when they are displayed again.
Default: false
keepDetailRowsCountSets the number of details rows to keep.
Default: 10

The example below demonstrates keeping detail rows. Note the following:

  • The detail grid has property keepDetailRows=true to turn on keeping detail rows.
  • The detail grid has property keepDetailRowsCount=2 which sets the number of details rows to keep to 2.
  • All the detail grids allow moving and sorting columns. If you change the state of a detail grid (e.g. by sorting a detail grid), that state will be kept if you close the parent row and then open the parent row again.
  • The maximum number of detail rows kept is two. If you open three detail grids and apply sorting on each grid, then close all three detail grids (so none are showing) and then open the three grids again, only two of the grids will have the sort state kept.

Changing Data & Refresh

By default when updating data using transactions then detail rows / grids get refreshed when there is new data for the master row they belong to. Other rows (where no data change accrued) do not get updated.

This refresh behaviour can be undesirable as the refresh will reset the detail grid loosing any context (eg sorting and filtering in the detail grid will be lost).

To change this behaviour, and never have the detail row / grid refresh, set suppressRefresh parameter on the detail cell renderer params.

The example below demonstrates changing data without refreshing the detail grids. Note the following:

  • The grid refreshes a row each second by incrementing the call count.
  • The detail grid never refreshes, thus any sort or filter applied will remain.
To fully understand this example, try opening it in Plunker and remove the suppressRefresh property.

Exporting Master / Detail Data

By default, exporting the master grid will just export the master rows. If you want to export detail rows, you can configure the getCustomContentBelowRow callback to generate a representation of the detail row that will be inserted below the master rows in the in the export. See the export documentation for more details.

There is an important difference between rendering and exporting Master / Detail content. When you expand a master row in the UI, a new instance of the Grid is created to render the detail, meaning that you have the full power of the Grid to sort, filter and format the detail data.

When exporting, the original data object representing the row is passed to getCustomContentBelowRow which returns styled content to be inserted into the export. No instance of the Grid is created. This means that export performance is good even with large Master / Detail data sets. However, if your detailGridOptions contains value getters, value formatters, sorting, filtering etc, and you want these to appear in the export, they must be applied by getCustomContentBelowRow.

Since detail grids are full Grid instances, triggering an export through the right-click context menu on a detail grid will do a normal export for the detail grid only. If this is not appropriate for your application you can disable the export item in the context menu, or replace it with a custom item that triggers an export on the master grid.

Example: Exporting Master / Detail Data

The example below demonstrate how both master and detail data can be exported.

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.


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.