---
title: "Aggregation - Total Rows"
enterprise: true
framework: angular
version: "36.1.0"
---

# Aggregation - Total Rows

This section shows how to include group and grand total rows in the grid.

## Enabling a Grand Total Row

A grand total row can be included in the grid by setting the `grandTotalRow` grid option to one of: `"top"`, `"bottom"`, `"pinnedTop"` or `"pinnedBottom"`.

Setting a value of `"top"` or `"bottom"` renders the grand total row as the first or last row in the grid, respectively. Setting a value of `"pinnedTop"` or `"pinnedBottom"` renders the grand total row pinned to the top or bottom of the grid, respectively.

> **Note**
>
> Grand total rows are also supported with the [Server-Side Row Model](https://www.ag-grid.com/angular-data-grid/server-side-model-grouping/#grand-total-row), including on flat grids without grouping.

#### Enabling Grand Total Row

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

ModuleRegistry.registerModules([
  ClientSideRowModelModule,
  RowGroupingModule,
  PinnedRowModule,
]);
import { IOlympicData } from "./interfaces";

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<div class="example-wrapper">
    <div class="example-header">
      <label>
        <span>grandTotalRow:</span>
        <select id="input-property-value" (change)="onChange()">
          <option value="bottom">"bottom"</option>
          <option value="top">"top"</option>
          <option value="pinnedBottom">"pinnedBottom"</option>
          <option value="pinnedTop">"pinnedTop"</option>
          <option value="undefined">undefined</option>
        </select>
      </label>
    </div>
    <ag-grid-angular
      style="width: 100%; height: 100%;"
      [columnDefs]="columnDefs"
      [defaultColDef]="defaultColDef"
      [autoGroupColumnDef]="autoGroupColumnDef"
      [grandTotalRow]="grandTotalRow"
      [rowData]="rowData"
      (gridReady)="onGridReady($event)"
    />
  </div> `,
})
export class AppComponent {
  private gridApi!: GridApi<IOlympicData>;

  columnDefs: ColDef[] = [
    { field: "country", rowGroup: true, hide: true },
    { field: "gold", aggFunc: "sum" },
    { field: "silver", aggFunc: "sum" },
    { field: "bronze", aggFunc: "sum" },
  ];
  defaultColDef: ColDef = {
    flex: 1,
    minWidth: 150,
  };
  autoGroupColumnDef: AutoGroupColumnDef = {
    minWidth: 300,
  };
  grandTotalRow: "top" | "bottom" | "pinnedTop" | "pinnedBottom" = "bottom";
  rowData!: IOlympicData[];

  constructor(private http: HttpClient) {}

  onChange() {
    const grandTotalRow = document.querySelector<HTMLInputElement>(
      "#input-property-value",
    )!.value;
    if (
      grandTotalRow === "bottom" ||
      grandTotalRow === "top" ||
      grandTotalRow === "pinnedTop" ||
      grandTotalRow === "pinnedBottom"
    ) {
      this.gridApi.setGridOption("grandTotalRow", grandTotalRow);
    } else {
      this.gridApi.setGridOption("grandTotalRow", undefined);
    }
  }

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

    this.http
      .get<
        IOlympicData[]
      >("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .subscribe((data) => (this.rowData = data));
  }
}
```

[Live example: Enabling Grand Total Row](https://www.ag-grid.com/examples/aggregation-total-rows/enabling-grand-total/angular)

The following configuration shows how grand total rows can be included at the bottom of the grid:

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

this.grandTotalRow = 'bottom';
```

## Enabling Group Total Rows

A total row can be included in every group when using [Row Grouping](https://www.ag-grid.com/angular-data-grid/grouping/) or [Tree Data](https://www.ag-grid.com/angular-data-grid/tree-data/) by setting the `groupTotalRow` grid option to either `"top"` or `"bottom"`. The provided value determines whether the total row will be included as the first or last row in the group.

#### Enabling Group Total Row

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

ModuleRegistry.registerModules([ClientSideRowModelModule, RowGroupingModule]);
import { IOlympicData } from "./interfaces";

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<div class="example-wrapper">
    <div class="example-header">
      <label>
        <span>groupTotalRow:</span>
        <select id="input-property-value" (change)="onChange()">
          <option value="bottom">"bottom"</option>
          <option value="top">"top"</option>
          <option value="undefined">undefined</option>
        </select>
      </label>
    </div>
    <ag-grid-angular
      style="width: 100%; height: 100%;"
      [columnDefs]="columnDefs"
      [defaultColDef]="defaultColDef"
      [autoGroupColumnDef]="autoGroupColumnDef"
      [groupDefaultExpanded]="groupDefaultExpanded"
      [groupTotalRow]="groupTotalRow"
      [rowData]="rowData"
      (gridReady)="onGridReady($event)"
    />
  </div> `,
})
export class AppComponent {
  private gridApi!: GridApi<IOlympicData>;

  columnDefs: ColDef[] = [
    { field: "country", rowGroup: true, hide: true },
    { field: "year", rowGroup: true, hide: true },
    { field: "gold", aggFunc: "sum" },
    { field: "silver", aggFunc: "sum" },
    { field: "bronze", aggFunc: "sum" },
  ];
  defaultColDef: ColDef = {
    flex: 1,
    minWidth: 150,
  };
  autoGroupColumnDef: AutoGroupColumnDef = {
    minWidth: 300,
  };
  groupDefaultExpanded = 1;
  groupTotalRow: "top" | "bottom" | UseGroupTotalRow = "bottom";
  rowData!: IOlympicData[];

  constructor(private http: HttpClient) {}

  onChange() {
    const groupTotalRow = document.querySelector<HTMLInputElement>(
      "#input-property-value",
    )!.value;
    if (groupTotalRow === "bottom" || groupTotalRow === "top") {
      this.gridApi.setGridOption("groupTotalRow", groupTotalRow);
    } else {
      this.gridApi.setGridOption("groupTotalRow", undefined);
    }
  }

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

    this.http
      .get<
        IOlympicData[]
      >("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .subscribe((data) => (this.rowData = data));
  }
}
```

[Live example: Enabling Group Total Row](https://www.ag-grid.com/examples/aggregation-total-rows/enabling-group-total/angular)

The following configuration shows how group total rows can be included at the bottom of every group:

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

// adds subtotals to the bottom of each row group
this.groupTotalRow = 'bottom';
```

### Selectively Display Group Total Rows

Total rows can be applied to certain groups selectively by providing a callback to the `groupTotalRow` grid option. This callback should return `"top"`, `"bottom"` or `undefined` and will be called for each row group to determine whether the group should display a total row.

#### Selectively Enabling Group Footers

```ts
import { Component } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { AgGridAngular } from "ag-grid-angular";
import {
  AutoGroupColumnDef,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  FirstDataRenderedEvent,
  GetGroupIncludeTotalRowParams,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  RowApiModule,
  UseGroupTotalRow,
  enableDevValidations,
} from "ag-grid-community";
import { RowGroupingModule } from "ag-grid-enterprise";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  RowApiModule,
  ClientSideRowModelModule,
  RowGroupingModule,
]);

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<ag-grid-angular
    style="width: 100%; height: 100%;"
    [columnDefs]="columnDefs"
    [defaultColDef]="defaultColDef"
    [autoGroupColumnDef]="autoGroupColumnDef"
    [groupTotalRow]="groupTotalRow"
    [rowData]="rowData"
    (firstDataRendered)="onFirstDataRendered($event)"
    (gridReady)="onGridReady($event)"
  /> `,
})
export class AppComponent {
  columnDefs: ColDef[] = [
    { field: "country", rowGroup: true, hide: true },
    { field: "year", rowGroup: true, hide: true },
    { field: "gold", aggFunc: "sum" },
    { field: "silver", aggFunc: "sum" },
    { field: "bronze", aggFunc: "sum" },
  ];
  defaultColDef: ColDef = {
    flex: 1,
    minWidth: 150,
  };
  autoGroupColumnDef: AutoGroupColumnDef = {
    minWidth: 300,
  };
  groupTotalRow: "top" | "bottom" | UseGroupTotalRow = (
    params: GetGroupIncludeTotalRowParams,
  ) => {
    const node = params.node;
    if (node && node.level === 1) return "bottom";
    if (node && node.key === "United States") return "bottom";
    return undefined;
  };
  rowData!: any[];

  constructor(private http: HttpClient) {}

  onFirstDataRendered(params: FirstDataRenderedEvent) {
    params.api.forEachNode((node) => {
      if (node.key === "United States" || node.key === "Russia") {
        params.api.setRowNodeExpanded(node, true);
      }
    });
  }

  onGridReady(params: GridReadyEvent) {
    this.http
      .get<any[]>("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .subscribe((data) => (this.rowData = data.slice(0, 50)));
  }
}
```

[Live example: Selectively Enabling Group Footers](https://www.ag-grid.com/examples/aggregation-total-rows/enabling-group-total-selectively/angular)

The example above demonstrates the following configuration to display total rows for the `"United States"` group, and the rows grouped by the `"year"` field:

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

this.groupTotalRow = (params) => {
    const node = params.node;
    if (node && node.level === 1) return 'bottom';
    if (node && node.key === 'United States') return 'bottom';
    return undefined;
};
```

### Keeping Group Row Values

When a total row is visible, the group row values are hidden. This behaviour can be prevented by setting the `groupSuppressBlankHeader` grid option to `true`.

#### Suppress Blank Groups

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

ModuleRegistry.registerModules([ClientSideRowModelModule, RowGroupingModule]);

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<div class="example-wrapper">
    <div class="example-header">
      <label>
        <span>groupSuppressBlankHeader:</span>
        <input
          id="groupSuppressBlankHeader"
          type="checkbox"
          (change)="toggleProperty()"
        />
      </label>
    </div>
    <ag-grid-angular
      style="width: 100%; height: 100%;"
      [columnDefs]="columnDefs"
      [defaultColDef]="defaultColDef"
      [autoGroupColumnDef]="autoGroupColumnDef"
      [groupTotalRow]="groupTotalRow"
      [groupDefaultExpanded]="groupDefaultExpanded"
      [rowData]="rowData"
      (gridReady)="onGridReady($event)"
    />
  </div> `,
})
export class AppComponent {
  private gridApi!: GridApi;

  columnDefs: ColDef[] = [
    { field: "country", rowGroup: true, hide: true },
    { field: "year", rowGroup: true, hide: true },
    { field: "gold", aggFunc: "sum" },
    { field: "silver", aggFunc: "sum" },
    { field: "bronze", aggFunc: "sum" },
  ];
  defaultColDef: ColDef = {
    flex: 1,
    minWidth: 150,
  };
  autoGroupColumnDef: AutoGroupColumnDef = {
    minWidth: 300,
  };
  groupTotalRow: "top" | "bottom" | UseGroupTotalRow = "bottom";
  groupDefaultExpanded = 1;
  rowData!: any[];

  constructor(private http: HttpClient) {}

  toggleProperty() {
    const enable = document.querySelector<HTMLInputElement>(
      "#groupSuppressBlankHeader",
    )!.checked;
    this.gridApi.setGridOption("groupSuppressBlankHeader", enable);
  }

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

    this.http
      .get<any[]>("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .subscribe((data) => (this.rowData = data));
  }
}
```

[Live example: Suppress Blank Groups](https://www.ag-grid.com/examples/aggregation-total-rows/suppress-blank-groups/angular)

The configuration below demonstrates the configuration for preventing the hiding of group row values:

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

this.groupSuppressBlankHeader = true;
```

## Group Column Cell Values

When using [Row Grouping](https://www.ag-grid.com/angular-data-grid/grouping-display-types/) or [Tree Data](https://www.ag-grid.com/angular-data-grid/tree-data-group-column/) with group columns, the group cell will display `"Total"` by default in the footer rows.

The default `agGroupCellRenderer.cellRendererParams` can be provided with a `totalValueGetter` to configure the value displayed in this cell.

#### Customising Footer Values

```ts
import { Component } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { AgGridAngular } from "ag-grid-angular";
import {
  AutoGroupColumnDef,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  UseGroupTotalRow,
  enableDevValidations,
} from "ag-grid-community";
import { RowGroupingModule } from "ag-grid-enterprise";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([ClientSideRowModelModule, RowGroupingModule]);

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<ag-grid-angular
    style="width: 100%; height: 100%;"
    [columnDefs]="columnDefs"
    [defaultColDef]="defaultColDef"
    [autoGroupColumnDef]="autoGroupColumnDef"
    [groupTotalRow]="groupTotalRow"
    [grandTotalRow]="grandTotalRow"
    [rowData]="rowData"
    (gridReady)="onGridReady($event)"
  /> `,
})
export class AppComponent {
  columnDefs: ColDef[] = [
    { field: "country", rowGroup: true, hide: true },
    { field: "year", rowGroup: true, hide: true },
    { field: "gold", aggFunc: "sum" },
    { field: "silver", aggFunc: "sum" },
    { field: "bronze", aggFunc: "sum" },
  ];
  defaultColDef: ColDef = {
    flex: 1,
    minWidth: 150,
  };
  autoGroupColumnDef: AutoGroupColumnDef = {
    minWidth: 300,
    cellRendererParams: {
      totalValueGetter: (params: any) => {
        const isRootLevel = params.node.level === -1;
        if (isRootLevel) {
          return "Grand Total";
        }
        return `Sub Total (${params.value})`;
      },
    },
  };
  groupTotalRow: "top" | "bottom" | UseGroupTotalRow = "bottom";
  grandTotalRow: "top" | "bottom" | "pinnedTop" | "pinnedBottom" = "bottom";
  rowData!: any[];

  constructor(private http: HttpClient) {}

  onGridReady(params: GridReadyEvent) {
    this.http
      .get<any[]>("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .subscribe((data) => (this.rowData = data));
  }
}
```

[Live example: Customising Footer Values](https://www.ag-grid.com/examples/aggregation-total-rows/customising-footer-values/angular)

The example above demonstrates using the following configuration to display custom group column values for grand total and group total rows:

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

this.autoGroupColumnDef = {
    cellRendererParams: {
        totalValueGetter: params =>  {
            const isRootLevel = params.node.level === -1;
            if (isRootLevel) {
                return 'Grand Total';
            }
            return `Sub Total (${params.value})`;
        },
    }
};
```

> **Note**
>
> When exporting, copying custom footers, or using Find with custom group cell values, the custom content must also be added using [processRowGroupCallback](https://www.ag-grid.com/angular-data-grid/excel-export-customising-content/) for export, [processCellForClipboard](https://www.ag-grid.com/angular-data-grid/clipboard/#processing-individual-cells) for copying to clipboard, or [getFindText](https://www.ag-grid.com/angular-data-grid/find/#using-find-with-cell-components) for Find.

## Suppress Sticky Rows

All total rows stick to the top or bottom of the viewport when scrolling. This behaviour can be configured by using the `suppressStickyTotalRow` grid option.

#### Suppress Sticky Total Rows

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

ModuleRegistry.registerModules([ClientSideRowModelModule, RowGroupingModule]);

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<div class="example-wrapper">
    <div class="example-header">
      <label>
        <span>suppressStickyTotalRow:</span>
        <select id="input-property-value" (change)="onChange()">
          <option value="false">false</option>
          <option value="true">true</option>
          <option value="grand">"grand"</option>
          <option value="group">"group"</option>
        </select>
      </label>
    </div>
    <ag-grid-angular
      style="width: 100%; height: 100%;"
      [columnDefs]="columnDefs"
      [defaultColDef]="defaultColDef"
      [autoGroupColumnDef]="autoGroupColumnDef"
      [groupDefaultExpanded]="groupDefaultExpanded"
      [groupTotalRow]="groupTotalRow"
      [grandTotalRow]="grandTotalRow"
      [rowData]="rowData"
      (gridReady)="onGridReady($event)"
    />
  </div> `,
})
export class AppComponent {
  private gridApi!: GridApi;

  columnDefs: ColDef[] = [
    { field: "country", rowGroup: true, hide: true },
    { field: "year", rowGroup: true, hide: true },
    { field: "gold", aggFunc: "sum" },
    { field: "silver", aggFunc: "sum" },
    { field: "bronze", aggFunc: "sum" },
  ];
  defaultColDef: ColDef = {
    flex: 1,
    minWidth: 150,
  };
  autoGroupColumnDef: AutoGroupColumnDef = {
    minWidth: 300,
  };
  groupDefaultExpanded = -1;
  groupTotalRow: "top" | "bottom" | UseGroupTotalRow = "bottom";
  grandTotalRow: "top" | "bottom" | "pinnedTop" | "pinnedBottom" = "bottom";
  rowData!: any[];

  constructor(private http: HttpClient) {}

  onChange() {
    const suppressStickyTotalRow = document.querySelector<HTMLInputElement>(
      "#input-property-value",
    )!.value;
    if (
      suppressStickyTotalRow === "grand" ||
      suppressStickyTotalRow === "group"
    ) {
      this.gridApi.setGridOption(
        "suppressStickyTotalRow",
        suppressStickyTotalRow,
      );
    } else if (suppressStickyTotalRow === "true") {
      this.gridApi.setGridOption("suppressStickyTotalRow", true);
    } else {
      this.gridApi.setGridOption("suppressStickyTotalRow", false);
    }
  }

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

    this.http
      .get<any[]>("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .subscribe((data) => (this.rowData = data));
  }
}
```

[Live example: Suppress Sticky Total Rows](https://www.ag-grid.com/examples/aggregation-total-rows/suppress-sticky-total-rows/angular)

The following configuration demonstrates how to suppress sticky behaviour for both grand and group total rows:

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

this.suppressStickyTotalRow = true;
```
