---
title: "SSRM Pivoting"
enterprise: true
framework: angular
version: "36.1.0"
---

# SSRM Pivoting

In this section we add Server-Side Pivoting to create an example with the ability to 'Slice and Dice' data using the Server-Side Row Model (SSRM).

## Enabling Pivoting

To pivot on a column `pivot=true` should be set on the column definition. Additionally, the grid needs to be in pivot mode which is set through the grid option `pivotMode=true`.

In the snippet below a pivot is defined on the 'year' column and pivot mode is enabled:

```ts
<ag-grid-angular
    [pivotMode]="pivotMode"
    [columnDefs]="columnDefs"
    /* other grid options ... */ />

// pivot mode enabled
this.pivotMode = true;
this.columnDefs = [
    { field: 'country', rowGroup: true },
    // pivot enabled
    { field: 'year', pivot: true },
    { field: 'total' },
];
```

For more configuration details see the section on [Pivoting](https://www.ag-grid.com/angular-data-grid/pivoting/).

## Pivoting on the Server

The actual pivoting is performed on the server when using the Server-Side Row Model. When the grid needs more rows it makes a request via `getRows(params)` on the [Server-Side Datasource](https://www.ag-grid.com/angular-data-grid/server-side-model-datasource/) with metadata containing row grouping details.

The properties relevant to pivoting in the request are shown below:

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `pivotCols` | `ColumnVO[]` |  |  | Columns that have pivot on them. |
| `pivotMode` | `boolean` |  |  | Defines if pivot mode is on or off. |

Note in the snippet above that `pivotCols` contains all the columns the grid is pivoting on, and `pivotMode` is used to determine if pivoting is currently enabled in the grid.

## Providing Pivot Result Columns

Pivot Result Columns are the columns that are created as part of the pivot function. You must provide these to the grid in order for the grid to display the correct columns for the active pivot function.

For instance, when pivoting on the `year` field, you must provide columns to the grid corresponding to each distinct year present in the data, such as `2000`, `2002`, `2004`, and so on.

### Supplying Pivot Result Fields (Simple)

The simplest way to provide pivot result columns is by supplying the fields containing your pivoted data to the `pivotResultFields` attribute in the `getRows` success callback. These fields are used to generate pivot result columns and appropriate column groups. By default, the grid expects the fields to be separated by an underscore (`'_'`), however, this can be altered via the `serverSidePivotResultFieldSeparator` grid option as shown below:

```ts
<ag-grid-angular
    [columnDefs]="columnDefs"
    [rowModelType]="rowModelType"
    [pivotMode]="pivotMode"
    [serverSidePivotResultFieldSeparator]="serverSidePivotResultFieldSeparator"
    /* other grid options ... */ />

this.columnDefs = [
    { field: 'country', rowGroup: true },
    { field: 'year', pivot: true }, // pivot on 'year'
    { field: 'gold', aggFunc: 'sum' },
    { field: 'silver', aggFunc: 'sum' },
    { field: 'bronze', aggFunc: 'sum' },
];
this.rowModelType = 'serverSide';
this.pivotMode = true;
// specify the field separator, e.g. '2000_gold' should be '_' which is also the default
this.serverSidePivotResultFieldSeparator = '_';
```

Note above that `serverSidePivotResultFieldSeparator` is not necessary as the default value is `'_'`.

The following snippet shows how to supply the `pivotResultFields` to the grid via the `success` callback:

```js
const createDatasource = server => {
    return {
        // called by the grid when more rows are required
        getRows: params => {

            // get data for request from server
            const response = server.getData(params.request);

            if (response.success) {
                // supply rows for requested block to grid
                params.success({
                    rowData: response.rows,
                    pivotResultFields: response.pivotFields, // ['2000_gold', '2000_silver',...]
                });
            } else {
                // inform grid request failed
                params.fail();
            }
        }
    };
}
```

The example below demonstrates this, note the following:

- The pivot fields are returned from the server and then passed to the grid via the `getRows` success callback via the `pivotResultFields` property. These are logged to the console as a demonstration.
- The grid splits the `pivotResultFields` by `_` and creates the pivot result columns and column groups where the generated columns use the provided fields to access the data from the rows.

#### Supplying Pivot Result Fields

```ts
import { Component } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { AgGridAngular } from "ag-grid-angular";
import {
  AutoGroupColumnDef,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  IServerSideDatasource,
  ModuleRegistry,
  RowModelType,
  SideBarDef,
  enableDevValidations,
} from "ag-grid-community";
import {
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  RowGroupingModule,
  RowGroupingPanelModule,
  ServerSideRowModelModule,
} from "ag-grid-enterprise";
import { FakeServer } from "./fakeServer";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  ColumnsToolPanelModule,
  ColumnMenuModule,
  ContextMenuModule,
  RowGroupingModule,
  RowGroupingPanelModule,
  ServerSideRowModelModule,
]);
import { IOlympicData } from "./interfaces";

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<ag-grid-angular
    style="width: 100%; height: 100%;"
    [columnDefs]="columnDefs"
    [defaultColDef]="defaultColDef"
    [autoGroupColumnDef]="autoGroupColumnDef"
    [rowModelType]="rowModelType"
    [pivotMode]="true"
    [sideBar]="sideBar"
    [rowGroupPanelShow]="rowGroupPanelShow"
    [pivotPanelShow]="pivotPanelShow"
    [serverSidePivotResultFieldSeparator]="serverSidePivotResultFieldSeparator"
    [suppressAggFuncInHeader]="true"
    [rowData]="rowData"
    (gridReady)="onGridReady($event)"
  /> `,
})
export class AppComponent {
  columnDefs: ColDef[] = [
    { field: "country", rowGroup: true, enableRowGroup: true },
    { field: "sport", enableRowGroup: true },
    { field: "year", pivot: true, enablePivot: true }, // pivot on 'year'
    { field: "gold", aggFunc: "sum", enableValue: true },
    { field: "silver", aggFunc: "sum", enableValue: true },
    { field: "bronze", aggFunc: "sum", enableValue: true },
  ];
  defaultColDef: ColDef = {
    flex: 1,
    minWidth: 100,
  };
  autoGroupColumnDef: AutoGroupColumnDef = {
    minWidth: 200,
  };
  rowModelType: RowModelType = "serverSide";
  sideBar: SideBarDef | string | string[] | boolean | null = {
    toolPanels: ["columns"],
  };
  rowGroupPanelShow: "always" | "onlyWhenGrouping" | "never" = "always";
  pivotPanelShow: "always" | "onlyWhenPivoting" | "never" = "always";
  serverSidePivotResultFieldSeparator = "_";
  rowData!: IOlympicData[];

  constructor(private http: HttpClient) {}

  onGridReady(params: GridReadyEvent<IOlympicData>) {
    this.http
      .get<
        IOlympicData[]
      >("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .subscribe((data) => {
        // setup the fake server with entire dataset
        const fakeServer = new FakeServer(data);
        // create datasource with a reference to the fake server
        const datasource = getServerSideDatasource(fakeServer);
        // register the datasource with the grid
        params.api!.setGridOption("serverSideDatasource", datasource);
      });
  }
}

function getServerSideDatasource(server: any): IServerSideDatasource {
  return {
    getRows: (params) => {
      console.log("[Datasource] - rows requested by grid: ", params.request);
      // get data for request from our fake server
      const response = server.getData(params.request);
      // simulating real server call with a 500ms delay
      setTimeout(() => {
        if (response.success) {
          // supply data to grid
          console.log(
            "[Datasource] - pivotResultFields to be set in grid: ",
            response.pivotFields,
          );
          params.success({
            rowData: response.rows,
            rowCount: response.lastRow,
            pivotResultFields: response.pivotFields,
          });
        } else {
          params.fail();
        }
      }, 500);
    },
  };
}
```

[Live example: Supplying Pivot Result Fields](https://www.ag-grid.com/examples/server-side-model-pivoting/supplying_pivot_result_fields/angular)

> **Note**
>
> When using managed columns, you can use [Pivot Callbacks](https://www.ag-grid.com/angular-data-grid/pivoting-result-columns/#column-definitions) to customise the pivot result column definitions.

### Creating Pivot Result Columns (Advanced)

It is also possible to create your own pivot result columns and provide them to the grid. This offers complete flexibility but can become complex when column groups are involved.

Pivot result columns are defined identically to the columns supplied to the grid options: you provide a list of [Column Definitions](https://www.ag-grid.com/angular-data-grid/column-definitions/) passing a list of columns and / or column groups using the following grid API method:

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `setPivotResultColumns` | `Function` |  |  | Set explicit pivot column definitions yourself. Used for advanced use cases only. Module: [`PivotModule`](https://www.ag-grid.com/angular-data-grid/modules/). |

There is no limit or restriction as to the number of columns or groups you pass. However, it's important that the field (or value getter) that you set for the columns match.

Here is how pivot result columns can be created and supplied to the grid via `setPivotResultColumns`:

```js
const createDatasource = server => {
    return {
        // called by the grid when more rows are required
        getRows: params => {

            // get data for request from server
            const response = server.getData(params.request);

            // add pivot result cols to the grid
            addPivotResultCols(response, params.api)

            if (response.success) {
                // supply rows for requested block to grid
                params.success({
                    rowData: response.rows,
                });
            } else {
                // inform grid request failed
                params.fail();
            }
        }
    };
}

function addPivotResultCols(response, api) {
    // create colDefs
    var pivotColDefs = response.pivotFields.map(function (field) {
        var headerName = field.split('_')[0]
        return { headerName: headerName, field: field }
    })

    // supply pivot result columns to the grid
    api.setPivotResultColumns(pivotColDefs)
}
```

In the code above, `addPivotResultCols` does not create column groups for simplicity. However, the example below shows a more complex implementation that creates column groups. Note the following:

- Column definitions are created from the `pivotFields` are returned from the server.
- These column definitions are then supplied to the grid via `api.setPivotResultColumns()`.

#### Creating Pivot Result Columns

```ts
import { Component } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { AgGridAngular } from "ag-grid-angular";
import {
  AutoGroupColumnDef,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  IServerSideDatasource,
  IServerSideGetRowsRequest,
  ModuleRegistry,
  RowModelType,
  SideBarDef,
  enableDevValidations,
} from "ag-grid-community";
import {
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  RowGroupingModule,
  RowGroupingPanelModule,
  ServerSideRowModelModule,
} from "ag-grid-enterprise";
import { FakeServer } from "./fakeServer";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  ColumnsToolPanelModule,
  ColumnMenuModule,
  ContextMenuModule,
  RowGroupingModule,
  RowGroupingPanelModule,
  ServerSideRowModelModule,
]);
import { IOlympicData } from "./interfaces";

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<ag-grid-angular
    style="width: 100%; height: 100%;"
    [columnDefs]="columnDefs"
    [defaultColDef]="defaultColDef"
    [autoGroupColumnDef]="autoGroupColumnDef"
    [rowModelType]="rowModelType"
    [pivotMode]="true"
    [sideBar]="sideBar"
    [rowGroupPanelShow]="rowGroupPanelShow"
    [pivotPanelShow]="pivotPanelShow"
    [rowData]="rowData"
    (gridReady)="onGridReady($event)"
  /> `,
})
export class AppComponent {
  columnDefs: ColDef[] = [
    { field: "country", rowGroup: true, enableRowGroup: true },
    { field: "sport", enableRowGroup: true },
    { field: "year", pivot: true, enablePivot: true }, // pivot on 'year'
    { field: "gold", aggFunc: "sum", enableValue: true },
    { field: "silver", aggFunc: "sum", enableValue: true },
    { field: "bronze", aggFunc: "sum", enableValue: true },
  ];
  defaultColDef: ColDef = {
    flex: 1,
    minWidth: 100,
  };
  autoGroupColumnDef: AutoGroupColumnDef = {
    minWidth: 200,
  };
  rowModelType: RowModelType = "serverSide";
  sideBar: SideBarDef | string | string[] | boolean | null = {
    toolPanels: ["columns"],
  };
  rowGroupPanelShow: "always" | "onlyWhenGrouping" | "never" = "always";
  pivotPanelShow: "always" | "onlyWhenPivoting" | "never" = "always";
  rowData!: IOlympicData[];

  constructor(private http: HttpClient) {}

  onGridReady(params: GridReadyEvent<IOlympicData>) {
    this.http
      .get<
        IOlympicData[]
      >("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .subscribe((data) => {
        // setup the fake server with entire dataset
        const fakeServer = new FakeServer(data);
        // create datasource with a reference to the fake server
        const datasource = getServerSideDatasource(fakeServer);
        // register the datasource with the grid
        params.api!.setGridOption("serverSideDatasource", datasource);
      });
  }
}

function getServerSideDatasource(server: any): IServerSideDatasource {
  return {
    getRows: (params) => {
      const request = params.request;
      console.log("[Datasource] - rows requested by grid: ", params.request);
      const response = server.getData(request);
      // add pivot results cols to the grid
      addPivotResultCols(request, response, params.api);
      // simulating real server call with a 500ms delay
      setTimeout(() => {
        if (response.success) {
          // supply data to grid
          params.success({
            rowData: response.rows,
            rowCount: response.lastRow,
          });
        } else {
          params.fail();
        }
      }, 500);
    },
  };
}
function addPivotResultCols(
  request: IServerSideGetRowsRequest,
  response: any,
  api: GridApi,
) {
  // check if pivot colDefs already exist
  const existingPivotColDefs = api.getPivotResultColumns();
  if (existingPivotColDefs && existingPivotColDefs.length > 0) {
    return;
  }
  // create pivot colDef's based of data returned from the server
  const pivotResultColumns = createPivotResultColumns(
    request,
    response.pivotFields,
  );
  // supply pivot result columns to the grid
  api.setPivotResultColumns(pivotResultColumns);
}
function addColDef(
  colId: string,
  parts: string[],
  res: (ColDef | ColGroupDef)[],
  request: IServerSideGetRowsRequest,
): (ColDef | ColGroupDef)[] {
  if (parts.length === 0) return [];
  const first = parts[0];
  const existing: ColGroupDef = res.find(
    (r: ColDef | ColGroupDef) => "groupId" in r && r.groupId === first,
  ) as ColGroupDef;
  if (existing) {
    existing["children"] = addColDef(
      colId,
      parts.slice(1),
      existing.children,
      request,
    );
  } else {
    const colDef: any = {};
    const isGroup = parts.length > 1;
    if (isGroup) {
      colDef["groupId"] = first;
      colDef["headerName"] = first;
    } else {
      const valueCol = request.valueCols.find((r) => r.field === first);
      if (valueCol) {
        colDef["colId"] = colId;
        colDef["headerName"] = valueCol.displayName;
        colDef["field"] = colId;
      }
    }
    const children = addColDef(colId, parts.slice(1), [], request);
    if (children.length > 0) {
      colDef["children"] = children;
    }
    res.push(colDef);
  }
  return res;
}
// The supplied order is the pivot result columns' natural order, used when the YEAR pill in the pivot panel is
// cycled to no sort. This example supplies the year groups shuffled so that order is distinguishable from asc/desc.
// Only the groups move, so Gold/Silver/Bronze keep their order within each year.
function shuffleYearGroups(yearGroups: ColGroupDef[]): ColGroupDef[] {
  return yearGroups
    .map((group) => ({ group, rank: window.agRandom() }))
    .sort((a, b) => a.rank - b.rank)
    .map((entry) => entry.group);
}
function createPivotResultColumns(
  request: IServerSideGetRowsRequest,
  pivotFields: string[],
): ColGroupDef[] {
  if (request.pivotMode && request.pivotCols.length > 0) {
    const pivotResultCols: ColGroupDef[] = [];
    pivotFields.forEach((field) =>
      addColDef(field, field.split("_"), pivotResultCols, request),
    );
    return shuffleYearGroups(pivotResultCols);
  }
  return [];
}
```

[Live example: Creating Pivot Result Columns](https://www.ag-grid.com/examples/server-side-model-pivoting/creating_pivot_result_columns/angular)

> **Note**
>
> You can control the order of the pivot result columns by sorting the `pivotColDefs` array before passing it to `api.setPivotResultColumns(pivotColDefs)`. That supplied order is the natural order, and it is what the grid shows until [Pivot Column Sorting](https://www.ag-grid.com/angular-data-grid/pivoting-column-groups/#sorting-pivot-columns) is applied - so unlike generated pivot result columns, supplied ones default to no sort rather than ascending. An explicit `pivotSort` of `'asc'` or `'desc'` orders the supplied column groups by header name instead, so sorting from a pivot column's pill reorders them without the grid asking the server for the columns again.

## Example: Pivot Column Groups

The example below demonstrates server-side Pivoting with multiple row groups where there are multiple value columns ('gold', 'silver', 'bronze') under the 'year' pivot column group. Note the following:

- Pivot mode is enabled through the grid option `pivotMode=true`.
- A pivot is placed on the **Year** column via `pivot=true` defined on the column definition.
- Rows are grouped by **Country** and **Sport** with `rowGroup=true` defined on their column definitions.
- The **Gold**, **Silver** and **Bronze** value columns have `aggFunc='sum'` defined on their column definitions.
- The `pivotCols` and `pivotMode` properties in the request are used by the server to perform pivoting.
- New column group definitions are generated from the `pivotResultFields` provided by the success callback.
- Open the browser's dev console to view the request supplied to the datasource.

#### Pivot Column Groups

```ts
import { Component } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { AgGridAngular } from "ag-grid-angular";
import "./styles.css";
import {
  AutoGroupColumnDef,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  IServerSideDatasource,
  ModuleRegistry,
  ProcessPivotResultColDef,
  RowModelType,
  enableDevValidations,
} from "ag-grid-community";
import {
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  RowGroupingModule,
  ServerSideRowModelModule,
} from "ag-grid-enterprise";
import { FakeServer } from "./fakeServer";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  ColumnsToolPanelModule,
  ColumnMenuModule,
  ContextMenuModule,
  RowGroupingModule,
  ServerSideRowModelModule,
]);
import { IOlympicData } from "./interfaces";

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<div class="example-wrapper">
    <div style="margin-bottom: 5px">
      <button (click)="expand('2000', true)">Expand 2000</button>
      <button (click)="expand('2000')">Collapse 2000</button>
      <button (click)="expand(undefined, true)">Expand All</button>
      <button (click)="expand(undefined)">Collapse All</button>
    </div>
    <ag-grid-angular
      style="width: 100%; height: 100%;"
      [columnDefs]="columnDefs"
      [defaultColDef]="defaultColDef"
      [autoGroupColumnDef]="autoGroupColumnDef"
      [rowModelType]="rowModelType"
      [pivotMode]="true"
      [processPivotResultColDef]="processPivotResultColDef"
      [rowData]="rowData"
      (gridReady)="onGridReady($event)"
    />
  </div> `,
})
export class AppComponent {
  private gridApi!: GridApi<IOlympicData>;

  columnDefs: ColDef[] = [
    { field: "country", rowGroup: true },
    { field: "sport", rowGroup: true },
    { field: "year", pivot: true }, // pivot on 'year'
    { field: "total", aggFunc: "sum" },
    { field: "gold", aggFunc: "sum" },
    { field: "silver", aggFunc: "sum" },
    { field: "bronze", aggFunc: "sum" },
  ];
  defaultColDef: ColDef = {
    width: 150,
  };
  autoGroupColumnDef: AutoGroupColumnDef = {
    minWidth: 200,
  };
  rowModelType: RowModelType = "serverSide";
  processPivotResultColDef: ProcessPivotResultColDef = (colDef: ColDef) => {
    const pivotValueColumn = colDef.pivotValueColumn;
    if (!pivotValueColumn) return;
    // if column is not the total column, it should only be shown when expanded.
    // this will enable expandable column groups.
    if (pivotValueColumn.getColId() !== "total") {
      colDef.columnGroupShow = "open";
    }
  };
  rowData!: IOlympicData[];

  constructor(private http: HttpClient) {}

  expand(key?: string, open = false) {
    if (key) {
      this.gridApi.setColumnGroupState([{ groupId: key, open: open }]);
      return;
    }
    const existingState = this.gridApi.getColumnGroupState();
    const expandedState = existingState.map(
      (s: { groupId: string; open: boolean }) => ({
        groupId: s.groupId,
        open: open,
      }),
    );
    this.gridApi.setColumnGroupState(expandedState);
  }

  onGridReady(params: GridReadyEvent<IOlympicData>) {
    this.gridApi = params.api;

    this.http
      .get<
        IOlympicData[]
      >("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .subscribe((data) => {
        // setup the fake server with entire dataset
        const fakeServer = new FakeServer(data);
        // create datasource with a reference to the fake server
        const datasource = getServerSideDatasource(fakeServer);
        // register the datasource with the grid
        params.api!.setGridOption("serverSideDatasource", datasource);
      });
  }
}

function getServerSideDatasource(server: any): IServerSideDatasource {
  return {
    getRows: (params) => {
      const request = params.request;
      console.log("[Datasource] - rows requested by grid: ", params.request);
      const response = server.getData(request);
      // simulating real server call with a 500ms delay
      setTimeout(() => {
        if (response.success) {
          // supply data to grid
          params.success({
            rowData: response.rows,
            rowCount: response.lastRow,
            pivotResultFields: response.pivotFields,
          });
        } else {
          params.fail();
        }
      }, 500);
    },
  };
}
```

[Live example: Pivot Column Groups](https://www.ag-grid.com/examples/server-side-model-pivoting/pivot-column-groups/angular)

## Example: Slice and Dice

A mock data store running inside the browser is used in the example below. The purpose of the mock server is to demonstrate the interaction between the grid and the server. For your application, your server will need to understand the requests from the client and build SQL (or the SQL equivalent if using a no-SQL data store) to run the relevant query against the data store.

The example demonstrates the following:

- Columns `Athlete, Age, Country, Year` and `Sport` all have `enableRowGroup=true` which means they can be grouped on. To group, you drag the columns to the row group panel section. By default the example is grouping by `Country` and then `Year` as these columns have `rowGroup=true`.
- Columns `Gold, Silver` and `Bronze` all have `enableValue=true` which means they can be aggregated on. To aggregate, you drag the column to the `Values` section. When you are grouping, all columns in the `Values` section will be aggregated.
- You can turn the grid into **Pivot Mode**. To do this, you click the pivot mode checkbox. When the grid is in pivot mode, the grid behaves similarly to an Excel grid. This extra information is passed to your server as part of the request and it is your server's responsibility to return the data in the correct structure.
- Columns `Age, Country, Year` and `Sport` all have `enablePivot=true` which means they can be pivoted on when **Pivot Mode** is active. To pivot, you drag the column to the **Pivot** section.
- Note that when you pivot, it is not possible to drill all the way down the leaf levels.
- In addition to grouping, aggregation and pivot, the example also demonstrates filtering. The columns **Country** and **Year** have grid-provided filters. The column **Age** has an example-provided custom filter. You can use whatever filter you want, as long as your server knows what to do with it.

#### Slice And Dice

```ts
import { Component } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { AgGridAngular } from "ag-grid-angular";
import {
  AutoGroupColumnDef,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  NumberFilterModule,
  RowModelType,
  SideBarDef,
  enableDevValidations,
} from "ag-grid-community";
import {
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  FiltersToolPanelModule,
  RowGroupingModule,
  RowGroupingPanelModule,
  ServerSideRowModelModule,
  SetFilterModule,
} from "ag-grid-enterprise";
import { getCountries } from "./countries";
import { createFakeServer, createServerSideDatasource } from "./server";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  NumberFilterModule,
  ColumnsToolPanelModule,
  FiltersToolPanelModule,
  ColumnMenuModule,
  ContextMenuModule,
  RowGroupingModule,
  ServerSideRowModelModule,
  SetFilterModule,
  RowGroupingPanelModule,
]);
import { IOlympicData } from "./interfaces";

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<ag-grid-angular
    style="width: 100%; height: 100%;"
    [columnDefs]="columnDefs"
    [defaultColDef]="defaultColDef"
    [autoGroupColumnDef]="autoGroupColumnDef"
    [rowModelType]="rowModelType"
    [rowGroupPanelShow]="rowGroupPanelShow"
    [pivotPanelShow]="pivotPanelShow"
    [sideBar]="true"
    [maxConcurrentDatasourceRequests]="maxConcurrentDatasourceRequests"
    [maxBlocksInCache]="maxBlocksInCache"
    [purgeClosedRowNodes]="true"
    [rowData]="rowData"
    (gridReady)="onGridReady($event)"
  /> `,
})
export class AppComponent {
  columnDefs: ColDef[] = [
    { field: "athlete", enableRowGroup: true, filter: false },
    {
      field: "age",
      enableRowGroup: true,
      enablePivot: true,
      filter: "agNumberColumnFilter",
      filterParams: {
        filterOptions: ["equals", "lessThan", "greaterThan"],
        maxNumConditions: 1,
      },
    },
    {
      field: "country",
      enableRowGroup: true,
      enablePivot: true,
      rowGroup: true,
      hide: true,
      filter: "agSetColumnFilter",
      filterParams: { values: countries },
    },
    {
      field: "year",
      enableRowGroup: true,
      enablePivot: true,
      rowGroup: true,
      hide: true,
      filter: "agSetColumnFilter",
      filterParams: {
        values: ["2000", "2002", "2004", "2006", "2008", "2010", "2012"],
      },
    },
    { field: "sport", enableRowGroup: true, enablePivot: true, filter: false },
    { field: "gold", aggFunc: "sum", filter: false, enableValue: true },
    { field: "silver", aggFunc: "sum", filter: false, enableValue: true },
    { field: "bronze", aggFunc: "sum", filter: false, enableValue: true },
  ];
  defaultColDef: ColDef = {
    flex: 1,
    minWidth: 150,
    // restrict what aggregation functions the columns can have,
    // include a custom function 'random' that just returns a
    // random number
    allowedAggFuncs: ["sum", "min", "max", "random"],
    filter: true,
  };
  autoGroupColumnDef: AutoGroupColumnDef = {
    width: 180,
  };
  rowModelType: RowModelType = "serverSide";
  rowGroupPanelShow: "always" | "onlyWhenGrouping" | "never" = "always";
  pivotPanelShow: "always" | "onlyWhenPivoting" | "never" = "always";
  maxConcurrentDatasourceRequests = 1;
  maxBlocksInCache = 2;
  rowData!: IOlympicData[];

  constructor(private http: HttpClient) {}

  onGridReady(params: GridReadyEvent<IOlympicData>) {
    this.http
      .get<
        IOlympicData[]
      >("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .subscribe((data) => {
        const fakeServer = createFakeServer(data);
        const datasource = createServerSideDatasource(fakeServer);
        params.api!.setGridOption("serverSideDatasource", datasource);
      });
  }
}

const countries = getCountries();
```

[Live example: Slice And Dice](https://www.ag-grid.com/examples/server-side-model-pivoting/slice-and-dice/angular)

## Batching Data Requests

You can stage multiple data requests by using the Columns Tool Panel. This allows multiple configuration changes to be applied in a single update, avoiding unnecessary intermediate recomputations or server requests. See [SSRM Row Grouping — Deferred Column Configuration](https://www.ag-grid.com/angular-data-grid/server-side-model-grouping/#deferred-column-configuration) for a detailed example.
