Row Grouping

This section covers Server-side Row Grouping using the Server-side Row Model.

Perhaps the most compelling reason to choose the Server-side Row Model is to achieve lazy-loading of Row Groups. This section will continue where Infinite Scroll left off, to cover a more complex scenario which includes Server-side Row Grouping, Aggregation and Sorting.

Group Caches

The Server-side Cache has already been covered, however in when performing Row Grouping it is important to understand that each group node contains a cache. This is illustrated in the following diagram:

When a group node is expanded, such as 'Australia' above, a cache will be created and blocks containing rows will be loaded via the Server-side Datasource

Group Request Parameters

The relevant IServerSideGetRowsRequest parameters for Row Grouping are as follows:

IServerSideGetRowsRequest { // row group columns rowGroupCols: ColumnVO[]; // what groups the user is viewing groupKeys: string[]; ... // other params }

Where rowGroupCols contains all the columns (dimensions) the grid is grouping on, i.e. 'Country', 'Year', and groupKeys contains the list of group keys selected, i.e. ['Argentina', 2012].

Example - Pre-defined Grouping - Mocked Server

Below shows an example of pre-defined grouping using the olympic winners dataset. It is pre-defined as we set the grid with specific columns for row grouping and do not allow the user to change this. Then the datasource knows that the grid will either be asking for the top level country list OR the grid will be looking for winners for a particular country.

In your application, your server-side would know where to get the data based on what the user is looking for, eg it could go to a relational database table to get the list of countries and then a web service to get the winners for the country as the user expands the group (a web service to get the winners per country is improbable, however the example demonstrates you do not need to go to the same datastore for the different levels in the grid).

In the example, the work your server would do is mocked for demonstrations purposes (as the online examples are self contained and do not contact any servers).

The example demonstrates the following:

  • Grouping: The data is grouped by country.
  • Aggregation: The server always sum's gold, silver and bronze. The columns are not set as value columns, and hence the user cannot change the aggregation function via the column menu. The server just assumes if grouping, then these columns should be aggregated using a sum function.
  • Sorting: The sorting is done on the server-side. For example, sort by Athlete, then expand a group and you will see Athlete is sorted.
When the grid sort changes, only impacted rows will get reloaded. For example if grouping by Country and sort by Athlete changes, the top level Country groups will not get reloaded as sorting by Athlete will not impact the top level groups. To avoid this and always refresh top level groups regardless of which column was sorted, set grid property serverSideSortingAlwaysResets = true.

Providing Child Counts

By default, the grid will not show row counts beside the group names. If you do want row counts, you need to implement the getChildCount() callback for the grid. The callback provides you with the row data, it is your applications responsibility to know what the child row count is. The suggestion is you set this information into the row data item you provide to the grid.

gridOptions.getChildCount = function(data) { // in this example, the data has the child count // stored in the attribute 'childCount'. return data.childCount; };

Purging Groups

The grid has the following API to allow you to interact with the server-side cache.

Method Description
purgeServerSideCache(route)

Purges the cache. If you pass no parameters, then the top level cache is purged. To purge a child cache, then pass in the string of keys to get to the child cache. For example, to purge the cache two levels down under 'Canada' and then '2002', pass in the string array ['Canada','2002']. If you purge a cache, then all row nodes for that cache will be reset to the closed state, and all child caches will be destroyed.

getCacheBlockState() Returns an object representing the state of the cache. This is useful for debugging and understanding how the cache is working.

Below shows the API in action. The following can be noted:

  • Button Purge Everything purges the top level cache.
  • Button Purge [Canada] purges the Canada cache only. To see this in action, make sure you have Canada expanded.
  • Button Purge [Canada,2002] purges the 2002 cache under Canada only. To see this in action, make sure you have Canada and then 2002 expanded.
  • Button Print Block State prints the state of the blocks in the cache to the console.
  • The example implements getChildCount() to set the child count for each group. Your application is responsible for figuring out the child count (maybe it's an attribute you set on the data?), the example sets a random number.

Preserving Group State

It may be necessary to expand groups to a desired initial state or to restore the grid to a previous state after purging / reloading data.

This can be achieved by expanding row nodes as blocks are loaded in the Server-side Datasource. The following snippet outlines a possible approach:

function getRows(params) { // 1) get data from server var response = getServerResponse(params.request); // 2) call the success callback params.successCallback(response.rowsThisBlock, response.lastRow); // 3) to preserve group state we expand any previously expanded groups for this block rowsInThisBlock.forEach(row => { if (expandedGroupIds.indexOf(row.id) > -1) { gridOptions.api.getRowNode(row.id).setExpanded(true); } }); }

Notice that in step 3, newly loaded row nodes for the current block are expanded if they are defined in expandedGroupIds, which is an array of group keys maintained by the application. This will have a cascading effect as expanding a group will cause new block(s) to load.

In order to easily look up group row nodes, implementing the following callback is recommended: gridOptions.getRowNodeId().

In the example below, the following can be noted:

  • The grid has an initial expanded group state where: expandedGroupIds = ["Russia", "Russia-2002", "Ireland", "Ireland-2008"]
  • The group state is updated in expandedGroupIds by using listening to the grid event: RowGroupOpened.
  • Clicking the 'Purge Caches' button reloads data. Notice that the group state has been preserved.

Complex Columns

It is possible the data provided has composite objects, in which case it's more difficult for the grid to extract group names. This can be worked with using value getters or embedded fields (ie the field attribute has dot notation).

In the example below, all rows back are modified so that the rows looks something like this:

row = { // country field is complex object country: { name: 'Ireland', code: 'IRE' }, // year field is complex object year: { name: '2012', shortName: "'12" }, // other fields as normal ... };

Then the columns are set up so that country uses a valueGetter and year uses a field with dot notation, ie year.name

Next Up

Continue to the next section to learn how to perform Server-side Pivoting.