---
product: "AG Grid"
title: "Calculated Columns"
description: "Add columns to the Angular Data Grid whose values are calculated from other columns with a spreadsheet-style expression. Declare calculated columns in code, or let users create their own from the column menu."
enterprise: true
framework: angular
version: "36.3.0"
related:
    - title: "Configuration"
      url: "https://www.ag-grid.com/archive/36.3.0/angular-data-grid/configuration/"
    - title: "Column Headers"
      url: "https://www.ag-grid.com/archive/36.3.0/angular-data-grid/column-headers/"
    - title: "Column Groups"
      url: "https://www.ag-grid.com/archive/36.3.0/angular-data-grid/column-groups/"
    - title: "Column Sizing"
      url: "https://www.ag-grid.com/archive/36.3.0/angular-data-grid/column-sizing/"
    - title: "Column Moving"
      url: "https://www.ag-grid.com/archive/36.3.0/angular-data-grid/column-moving/"
    - title: "Column Pinning"
      url: "https://www.ag-grid.com/archive/36.3.0/angular-data-grid/column-pinning/"
    - title: "Column Spanning"
      url: "https://www.ag-grid.com/archive/36.3.0/angular-data-grid/column-spanning/"
llms: "https://www.ag-grid.com/archive/36.3.0/llms.txt"
---

# Calculated Columns

Calculated Columns let your end users add read-only values to the grid without storing those values in row data. Each calculated column has an expression, such as `[revenue] - [cost]`, that derives its value from other columns.

Declare calculated columns in your column definitions, or let users add their own from the column menu. To let users write expressions in individual cells instead, see [Formulas](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/formulas/).

## Enabling Calculated Columns

Set the `calculatedColumns` grid option to `true`, and set `calculatedExpression` on a column definition:

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

this.calculatedColumns = true;
this.columnDefs = [
    { field: 'revenue' },
    { field: 'cost' },
    {
        colId: 'profit',
        calculatedExpression: '[revenue] - [cost]',
        cellDataType: 'number',
        sortable: true,
        filter: true,
    },
];
```

> **Note**
>
> Calculated columns declared in `columnDefs` must define a `colId` explicitly.

#### Calculated Columns

```ts
import { Component } from "@angular/core";
import { AgGridAngular } from "ag-grid-angular";
import {
  CalculatedColumnsGridOption,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  DataTypeDefinitions,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  NumberEditorModule,
  NumberFilterModule,
  ValueFormatterLiteParams,
  enableDevValidations,
} from "ag-grid-community";
import { CalculatedColumnsModule, ColumnMenuModule } from "ag-grid-enterprise";

if (process.env.NODE_ENV !== "production") {
  // Enable extended validations only for development
  enableDevValidations();
}

ModuleRegistry.registerModules([
  ClientSideRowModelModule,
  CalculatedColumnsModule,
  ColumnMenuModule,
  NumberEditorModule,
  NumberFilterModule,
]);
import { SalesRow } from "./interfaces";

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<div style="height: 100%; box-sizing: border-box">
    <ag-grid-angular
      style="width: 100%; height: 100%;"
      [columnDefs]="columnDefs"
      [rowData]="rowData"
      [dataTypeDefinitions]="dataTypeDefinitions"
      [calculatedColumns]="calculatedColumns"
      [defaultColDef]="defaultColDef"
    />
  </div> `,
})
export class AppComponent {
  columnDefs: ColDef[] = [
    { field: "product", flex: 1 },
    {
      field: "revenue",
      editable: true,
      cellDataType: "currency",
    },
    {
      field: "cost",
      editable: true,
      cellDataType: "currency",
    },
    {
      colId: "profit",
      headerName: "Profit",
      calculatedExpression: "[revenue] - [cost]",
      cellDataType: "currency",
      sortable: true,
      filter: "agNumberColumnFilter",
    },
  ];
  rowData: SalesRow[] | null = [
    { product: "Solar panel kit", revenue: 142000, cost: 96000 },
    { product: "Smart thermostat", revenue: 78000, cost: 52000 },
    { product: "Battery pack", revenue: 126000, cost: 101000 },
    { product: "EV charger", revenue: 92000, cost: 61000 },
    { product: "Heat pump", revenue: 168000, cost: 119000 },
    { product: "Inverter unit", revenue: 88000, cost: 57000 },
    { product: "Wind turbine kit", revenue: 232000, cost: 171000 },
    { product: "Solar tile roof", revenue: 198000, cost: 144000 },
    { product: "Power optimiser", revenue: 64000, cost: 41000 },
    { product: "Charge controller", revenue: 53000, cost: 33000 },
    { product: "Energy monitor", revenue: 47000, cost: 29000 },
    { product: "Storage cabinet", revenue: 71000, cost: 52000 },
    { product: "Microinverter", revenue: 59000, cost: 37000 },
    { product: "Heat recovery unit", revenue: 124000, cost: 88000 },
    { product: "Hybrid boiler", revenue: 156000, cost: 117000 },
    { product: "Smart meter", revenue: 39000, cost: 24000 },
    { product: "Insulation pack", revenue: 44000, cost: 27000 },
    { product: "EV cable set", revenue: 31000, cost: 18000 },
    { product: "Solar pump", revenue: 67000, cost: 45000 },
    { product: "Backup generator", revenue: 173000, cost: 131000 },
  ];
  dataTypeDefinitions: DataTypeDefinitions = {
    currency: {
      baseDataType: "number",
      extendsDataType: "number",
      valueFormatter: currencyFormatter,
    },
  };
  calculatedColumns: CalculatedColumnsGridOption = {
    dataTypes: ["currency", "number", "text", "boolean"],
  };
  defaultColDef: ColDef = {
    flex: 1,
    minWidth: 130,
  };
}

const currencyFormatter = (
  params: ValueFormatterLiteParams<SalesRow, number>,
): string => {
  const { value } = params;
  if (value == null) {
    return "";
  }
  if (String(value).startsWith("#")) {
    return String(value);
  }
  return `$${value.toLocaleString()}`;
};
```

[Live example: Calculated Columns](https://www.ag-grid.com/archive/36.3.0/examples/calculated-columns/calculated-columns/angular/)

In the example, Profit is a calculated column. To create your own, choose **Add Calculated Column** from any column's header menu.

## Writing Expressions

In `calculatedExpression`, a reference in square brackets, such as `[revenue]`, resolves to the same-row value from the column with that `colId`. The value inside the brackets must match a column `colId`, which defaults to the `field` when no explicit `colId` is provided. Calculated expressions do not need a leading equals sign (`=`).

Calculated columns can use the same [Mathematical Operators](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/formula-reference/#mathematical-operators), [Provided Functions](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/formula-reference/#provided-functions) and [Custom Functions](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/formula-custom-functions/) as [formulas](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/formulas/).

### Chaining Calculated Columns

Calculated columns can reference other calculated columns in the same row, which lets you chain derived values such as profit, margin and status:

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

this.calculatedColumns = true;
this.columnDefs = [
    { field: 'revenue' },
    { field: 'cost' },
    {
        colId: 'profit',
        calculatedExpression: '[revenue] - [cost]',
        cellDataType: 'currency',
    },
    {
        colId: 'margin',
        calculatedExpression: '[profit] / [revenue]',
        cellDataType: 'percentage',
    },
    {
        colId: 'status',
        calculatedExpression: 'IF([margin] >= 0.25, "Healthy", "Review")',
        cellDataType: 'text',
    },
];
```

The `currency` and `percentage` types are registered with `dataTypeDefinitions`, as shown in [Data Types](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/calculated-columns-dialog/#datatypes).

#### Chaining Calculated Columns

```ts
import { Component } from "@angular/core";
import { AgGridAngular } from "ag-grid-angular";
import {
  CalculatedColumnsGridOption,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  DataTypeDefinitions,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  NumberFilterModule,
  TextFilterModule,
  ValueFormatterLiteParams,
  enableDevValidations,
} from "ag-grid-community";
import { CalculatedColumnsModule, ColumnMenuModule } from "ag-grid-enterprise";

if (process.env.NODE_ENV !== "production") {
  // Enable extended validations only for development
  enableDevValidations();
}

ModuleRegistry.registerModules([
  ClientSideRowModelModule,
  CalculatedColumnsModule,
  ColumnMenuModule,
  NumberFilterModule,
  TextFilterModule,
]);
import { SalesRow } from "./interfaces";

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<div style="height: 100%; box-sizing: border-box">
    <ag-grid-angular
      style="width: 100%; height: 100%;"
      [columnDefs]="columnDefs"
      [rowData]="rowData"
      [dataTypeDefinitions]="dataTypeDefinitions"
      [calculatedColumns]="calculatedColumns"
      [defaultColDef]="defaultColDef"
    />
  </div> `,
})
export class AppComponent {
  columnDefs: ColDef[] = [
    { field: "account", flex: 1.4 },
    {
      field: "revenue",
      cellDataType: "currency",
    },
    {
      field: "cost",
      cellDataType: "currency",
    },
    {
      colId: "profit",
      headerName: "Profit",
      calculatedExpression: "[revenue] - [cost]",
      cellDataType: "currency",
      sortable: true,
      filter: "agNumberColumnFilter",
    },
    {
      colId: "margin",
      headerName: "Margin",
      calculatedExpression: "[profit] / [revenue]",
      cellDataType: "percentage",
      sortable: true,
      filter: "agNumberColumnFilter",
    },
    {
      colId: "status",
      headerName: "Status",
      calculatedExpression: 'IF([margin] >= 0.25, "Healthy", "Review")',
      cellDataType: "text",
      sortable: true,
      filter: "agTextColumnFilter",
    },
  ];
  rowData: SalesRow[] | null = [
    { account: "Northwind Energy", revenue: 245000, cost: 172000 },
    { account: "Summit Retail", revenue: 186000, cost: 151000 },
    { account: "Pioneer Logistics", revenue: 214000, cost: 139000 },
    { account: "Apex Manufacturing", revenue: 198000, cost: 158000 },
    { account: "Blue River Telecom", revenue: 276000, cost: 192000 },
    { account: "Crestline Foods", revenue: 167000, cost: 121000 },
    { account: "Harbor Freight Co", revenue: 142000, cost: 99000 },
    { account: "Atlas Mining", revenue: 251000, cost: 197000 },
    { account: "Veridian Health", revenue: 173000, cost: 128000 },
    { account: "Quantum Software", revenue: 298000, cost: 176000 },
    { account: "Redwood Hotels", revenue: 132000, cost: 104000 },
    { account: "Ironbridge Steel", revenue: 221000, cost: 183000 },
    { account: "Lakeside Media", revenue: 96000, cost: 71000 },
    { account: "Polar Shipping", revenue: 188000, cost: 142000 },
    { account: "Granite Insurance", revenue: 204000, cost: 149000 },
    { account: "Cobalt Mining", revenue: 243000, cost: 186000 },
    { account: "Meridian Airlines", revenue: 312000, cost: 268000 },
    { account: "Oakfield Farms", revenue: 87000, cost: 62000 },
    { account: "Silverline Bank", revenue: 265000, cost: 191000 },
    { account: "Horizon Telecom", revenue: 154000, cost: 112000 },
  ];
  dataTypeDefinitions: DataTypeDefinitions = {
    currency: {
      baseDataType: "number",
      extendsDataType: "number",
      valueFormatter: currencyFormatter,
    },
    percentage: {
      baseDataType: "number",
      extendsDataType: "number",
      valueFormatter: percentageFormatter,
    },
  };
  calculatedColumns: CalculatedColumnsGridOption = {
    dataTypes: ["currency", "percentage", "number", "text", "boolean"],
  };
  defaultColDef: ColDef = {
    flex: 1,
    minWidth: 130,
  };
}

const formatter = (
  params: ValueFormatterLiteParams<SalesRow, number>,
  format: (value: number) => string,
): string => {
  const { value } = params;
  if (value == null) {
    return "";
  }
  if (String(value).startsWith("#")) {
    return String(value);
  }
  return format(value);
};
const currencyFormatter = (
  params: ValueFormatterLiteParams<SalesRow, number>,
) => formatter(params, (value) => `$${value.toLocaleString()}`);
const percentageFormatter = (
  params: ValueFormatterLiteParams<SalesRow, number>,
) => formatter(params, (value) => `${Math.round(value * 100)}%`);
```

[Live example: Chaining Calculated Columns](https://www.ag-grid.com/archive/36.3.0/examples/calculated-columns/calculated-columns-advanced/angular/)

## Letting Users Add Columns

Register the [Column Menu](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/column-menu/) so users can add, edit and remove calculated columns from the grid:

- **Add Calculated Column**, in the header menu of any column, opens the dialog to add a calculated column. It is not shown in pivot mode.
- **Edit Calculated Column**, in the header menu of a calculated column, opens the dialog to change its title, type and expression. A read-only column shows **View Calculated Column** instead.
- **Remove Calculated Column**, in the header menu of a calculated column and the context menu of its cells, removes the column. It is disabled when the column is read-only.

When adding a calculated column from a header menu or via the API with a source column, the new column uses the source column's current pinning. The new column appears next to the source: on the right in LTR and on the left in RTL.

In the [dialog](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/calculated-columns-dialog/), users refer to columns by title instead of `colId`, for example `[Revenue]`. See [Referencing Columns](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/calculated-columns-dialog/#references-with-column-groups) for how the dialog handles duplicate titles. Custom functions registered through `formulaFuncs` appear in the dialog's Functions picker and in autocomplete.

Register [Cell Data Types](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/cell-data-types/) to give users formatting options to pick from when they create a column. The first example on this page provides a `currency` type; see [Data Types](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/calculated-columns-dialog/#datatypes).

Calculated cell values cannot be changed through cell editing, paste, fill handle or delete operations. Users can still change a calculated column's title, type and expression through its dialog. To prevent these dialog edits and removal through the grid's menus, see [Read-Only Calculated Columns](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/calculated-columns-validation/#read-only-calculated-columns).

## Feature Interactions

Calculated Columns are supported with all row models. Calculated columns are full columns, so [Row Grouping](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/grouping/), [Tree Data](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/tree-data/), [Pivoting](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/pivoting/), [Sorting](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/row-sorting/), [Filtering](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/filtering/), [Text Formatting](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/value-formatters/) and [Cell Components](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/component-cell-renderer/) apply to them the same way as any other column.

For [Server-Side](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/server-side-model/), [Infinite](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/infinite-scrolling/) and [Viewport](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/viewport/) Row Models, sorting and filtering are datasource or server responsibilities. The grid displays calculated values for loaded rows, but does not client-sort or client-filter remote datasets by calculated results.

### Row Groups and Tree Data

A calculated column evaluates on leaf rows and on Tree Data parents that carry data. It stays blank on rows without data: row group rows, group footers, the grand total row and Tree Data filler nodes.

To show a value on group rows, give the calculated column an `aggFunc`. Each leaf evaluates the expression, and the group aggregates those per-leaf results with the chosen function, the same as a column with a `valueGetter`. For example, `{ calculatedExpression: '[revenue] - [cost]', aggFunc: 'sum' }` shows the total of its rows' profits on a group. A ratio has no meaningful aggregation, so a column like `[profit] / [revenue]` is left without an `aggFunc` and stays blank on group rows.

#### Row Groups with Calculated Columns

```ts
import { Component } from "@angular/core";
import { AgGridAngular } from "ag-grid-angular";
import {
  AutoGroupColumnDef,
  CalculatedColumnsGridOption,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  DataTypeDefinitions,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  NumberFilterModule,
  ValueFormatterLiteParams,
  enableDevValidations,
} from "ag-grid-community";
import {
  CalculatedColumnsModule,
  ColumnMenuModule,
  RowGroupingModule,
} from "ag-grid-enterprise";

if (process.env.NODE_ENV !== "production") {
  // Enable extended validations only for development
  enableDevValidations();
}

ModuleRegistry.registerModules([
  ClientSideRowModelModule,
  CalculatedColumnsModule,
  ColumnMenuModule,
  RowGroupingModule,
  NumberFilterModule,
]);
import { SalesRow } from "./interfaces";

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<div style="height: 100%; box-sizing: border-box">
    <ag-grid-angular
      style="width: 100%; height: 100%;"
      [columnDefs]="columnDefs"
      [rowData]="rowData"
      [dataTypeDefinitions]="dataTypeDefinitions"
      [calculatedColumns]="calculatedColumns"
      [defaultColDef]="defaultColDef"
      [autoGroupColumnDef]="autoGroupColumnDef"
      [groupDefaultExpanded]="groupDefaultExpanded"
    />
  </div> `,
})
export class AppComponent {
  columnDefs: ColDef[] = [
    { field: "productType", rowGroup: true, hide: true },
    { field: "product", flex: 1.4 },
    {
      field: "revenue",
      aggFunc: "sum",
      cellDataType: "currency",
    },
    {
      field: "cost",
      aggFunc: "sum",
      cellDataType: "currency",
    },
    {
      colId: "profit",
      headerName: "Profit",
      calculatedExpression: "[revenue] - [cost]",
      // aggFunc lets the calculated column aggregate its per-leaf results onto group rows.
      aggFunc: "sum",
      cellDataType: "currency",
      filter: "agNumberColumnFilter",
    },
    {
      colId: "margin",
      headerName: "Margin",
      // No aggFunc: a ratio does not aggregate, so margin evaluates on leaf rows and is blank on groups.
      calculatedExpression: "[profit] / [revenue]",
      cellDataType: "percentage",
    },
  ];
  rowData: SalesRow[] | null = [
    {
      productType: "Solar",
      product: "Solar panel kit",
      revenue: 142000,
      cost: 96000,
    },
    {
      productType: "Solar",
      product: "Smart thermostat",
      revenue: 78000,
      cost: 52000,
    },
    {
      productType: "Charging",
      product: "Battery pack",
      revenue: 126000,
      cost: 101000,
    },
    {
      productType: "Charging",
      product: "EV charger",
      revenue: 92000,
      cost: 61000,
    },
    {
      productType: "Heating",
      product: "Heat pump",
      revenue: 168000,
      cost: 119000,
    },
    {
      productType: "Heating",
      product: "Hybrid boiler",
      revenue: 156000,
      cost: 117000,
    },
    {
      productType: "Heating",
      product: "Heat recovery unit",
      revenue: 124000,
      cost: 88000,
    },
    {
      productType: "Storage",
      product: "Storage cabinet",
      revenue: 71000,
      cost: 52000,
    },
    {
      productType: "Storage",
      product: "Lithium rack",
      revenue: 143000,
      cost: 112000,
    },
    {
      productType: "Storage",
      product: "Flow battery",
      revenue: 187000,
      cost: 149000,
    },
    {
      productType: "Storage",
      product: "Backup generator",
      revenue: 173000,
      cost: 131000,
    },
    {
      productType: "Wind",
      product: "Wind turbine kit",
      revenue: 232000,
      cost: 171000,
    },
    {
      productType: "Wind",
      product: "Micro turbine",
      revenue: 76000,
      cost: 49000,
    },
    {
      productType: "Wind",
      product: "Tower mount",
      revenue: 41000,
      cost: 26000,
    },
    {
      productType: "Monitoring",
      product: "Energy monitor",
      revenue: 47000,
      cost: 29000,
    },
    {
      productType: "Monitoring",
      product: "Smart meter",
      revenue: 39000,
      cost: 24000,
    },
    {
      productType: "Monitoring",
      product: "Grid analyser",
      revenue: 58000,
      cost: 36000,
    },
    {
      productType: "Efficiency",
      product: "Insulation pack",
      revenue: 44000,
      cost: 27000,
    },
    {
      productType: "Efficiency",
      product: "LED retrofit",
      revenue: 36000,
      cost: 21000,
    },
    {
      productType: "Efficiency",
      product: "Window film",
      revenue: 28000,
      cost: 16000,
    },
  ];
  dataTypeDefinitions: DataTypeDefinitions = {
    currency: {
      baseDataType: "number",
      extendsDataType: "number",
      valueFormatter: currencyFormatter,
    },
    percentage: {
      baseDataType: "number",
      extendsDataType: "number",
      valueFormatter: percentageFormatter,
    },
  };
  calculatedColumns: CalculatedColumnsGridOption = {
    dataTypes: ["currency", "percentage", "number", "text", "boolean"],
  };
  defaultColDef: ColDef = {
    flex: 1,
    minWidth: 130,
  };
  autoGroupColumnDef: AutoGroupColumnDef = {
    headerName: "Product Type",
    minWidth: 180,
  };
  groupDefaultExpanded = -1;
}

const formatter = (
  params: ValueFormatterLiteParams<SalesRow, number>,
  format: (value: number) => string,
): string => {
  const { value } = params;
  if (value == null) {
    return "";
  }
  if (String(value).startsWith("#")) {
    return String(value);
  }
  return format(value);
};
const currencyFormatter = (
  params: ValueFormatterLiteParams<SalesRow, number>,
) => formatter(params, (value) => `$${value.toLocaleString()}`);
const percentageFormatter = (
  params: ValueFormatterLiteParams<SalesRow, number>,
) => formatter(params, (value) => `${Math.round(value * 100)}%`);
```

[Live example: Row Groups with Calculated Columns](https://www.ag-grid.com/archive/36.3.0/examples/calculated-columns/calculated-columns-row-groups/angular/)

In the example, Profit has an `aggFunc` and shows a total on each group row, while Margin stays blank there.

### Letting Users Choose an Aggregation

The dialog has no aggregation field. Setting `enableValue`, for example in `defaultColDef`, lets users choose an aggregation for any calculated column, including one created in the dialog. To allow it only on the columns users create, return `enableValue` from [processColDef](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/calculated-columns-dialog/#processcoldef) instead.

Users choose the aggregation from **Value Aggregation** in the column menu while rows are grouped, or by adding the column to the Values section of the [Columns Tool Panel](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/tool-panel-columns/#drop-zones). To limit the choice, set `allowedAggFuncs` as in the example below. [Configuring via the UI](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/aggregation-columns/#configuring-via-the-ui) covers this and `defaultAggFunc`.

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

this.calculatedColumns = true;
this.defaultColDef = {
    enableValue: true,
    allowedAggFuncs: ['sum', 'avg', 'min', 'max'],
};
this.sideBar = 'columns';
```

#### Letting Users Choose an Aggregation

```ts
import { Component } from "@angular/core";
import { AgGridAngular } from "ag-grid-angular";
import {
  AutoGroupColumnDef,
  CalculatedColumnsGridOption,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  SideBarDef,
  enableDevValidations,
} from "ag-grid-community";
import {
  CalculatedColumnsModule,
  ColumnMenuModule,
  ColumnsToolPanelModule,
  RowGroupingModule,
} from "ag-grid-enterprise";

if (process.env.NODE_ENV !== "production") {
  // Enable extended validations only for development
  enableDevValidations();
}

ModuleRegistry.registerModules([
  ClientSideRowModelModule,
  CalculatedColumnsModule,
  ColumnMenuModule,
  ColumnsToolPanelModule,
  RowGroupingModule,
]);
import { SalesRow } from "./interfaces";

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<div style="height: 100%; box-sizing: border-box">
    <ag-grid-angular
      style="width: 100%; height: 100%;"
      [columnDefs]="columnDefs"
      [rowData]="rowData"
      [calculatedColumns]="true"
      [defaultColDef]="defaultColDef"
      [autoGroupColumnDef]="autoGroupColumnDef"
      [sideBar]="sideBar"
      [groupDefaultExpanded]="groupDefaultExpanded"
    />
  </div> `,
})
export class AppComponent {
  columnDefs: ColDef[] = [
    { field: "productType", rowGroup: true, hide: true },
    { field: "product", flex: 1.4 },
    { field: "revenue", aggFunc: "sum" },
    { field: "cost", aggFunc: "sum" },
  ];
  rowData: SalesRow[] | null = [
    {
      productType: "Solar",
      product: "Solar panel kit",
      revenue: 142000,
      cost: 96000,
    },
    {
      productType: "Solar",
      product: "Smart thermostat",
      revenue: 78000,
      cost: 52000,
    },
    {
      productType: "Charging",
      product: "Battery pack",
      revenue: 126000,
      cost: 101000,
    },
    {
      productType: "Charging",
      product: "EV charger",
      revenue: 92000,
      cost: 61000,
    },
    {
      productType: "Heating",
      product: "Heat pump",
      revenue: 168000,
      cost: 119000,
    },
    {
      productType: "Heating",
      product: "Hybrid boiler",
      revenue: 156000,
      cost: 117000,
    },
    {
      productType: "Heating",
      product: "Heat recovery unit",
      revenue: 124000,
      cost: 88000,
    },
    {
      productType: "Storage",
      product: "Storage cabinet",
      revenue: 71000,
      cost: 52000,
    },
    {
      productType: "Storage",
      product: "Lithium rack",
      revenue: 143000,
      cost: 112000,
    },
    {
      productType: "Storage",
      product: "Flow battery",
      revenue: 187000,
      cost: 149000,
    },
    {
      productType: "Wind",
      product: "Wind turbine kit",
      revenue: 232000,
      cost: 171000,
    },
    {
      productType: "Wind",
      product: "Micro turbine",
      revenue: 76000,
      cost: 49000,
    },
  ];
  defaultColDef: ColDef = {
    flex: 1,
    minWidth: 130,
    // Lets users choose an aggregation for every column, including calculated columns created in the dialog.
    enableValue: true,
    allowedAggFuncs: ["sum", "avg", "min", "max"],
  };
  autoGroupColumnDef: AutoGroupColumnDef = {
    headerName: "Product Type",
    minWidth: 180,
  };
  sideBar: SideBarDef | string | string[] | boolean | null = "columns";
  groupDefaultExpanded = -1;
}
```

[Live example: Letting Users Choose an Aggregation](https://www.ag-grid.com/archive/36.3.0/examples/calculated-columns/calculated-columns-aggregation/angular/)

In the example, choose **Add Calculated Column** from the Cost header menu and enter `[Revenue] - [Cost]` as the expression. Close the dialog, then open **Value Aggregation** in the new column's header menu. It offers only None, Sum, Average, Min and Max.

### Pivoting

Under [Pivoting](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/pivoting/), a calculated column behaves the same as a column with a `valueGetter`:

- With an `aggFunc`, it is a value column. It evaluates per leaf, and its results aggregate into the pivot result columns.
- Without an `aggFunc`, it is not a value column, so it has no pivot result column and is absent from the cross-tab, like any other non-value column.
- As a pivot column (`pivot: true`), its per-row result becomes the pivot key, so the grid creates a result column for each distinct calculated value.

Users cannot add calculated columns while pivot mode is on.

## Styling

Header cells and grid cells for calculated columns include the `ag-calculated-column` CSS class; use it for custom styling. To change the highlight shown while a column's dialog is open, see [Column Highlighting](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/calculated-columns-dialog/#suppresscolumnhighlighting).

## Next Steps

- [Dialog & Customisation](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/calculated-columns-dialog/): opening the dialog, column references, the expression editor, data types, when edits apply, and customising the columns users create.
- [Restrictions & Validation](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/calculated-columns-validation/): read-only columns, hidden menu items, restricted column references, custom validation and refreshing.
- [API, Events & State](https://www.ag-grid.com/archive/36.3.0/angular-data-grid/calculated-columns-api/): managing calculated columns in code, saving and restoring them, events, and the API reference.
