The grid supports editing grouped data when using the Client-Side Row Model. This page covers making group row cells editable, distributing edited values to descendant rows, and refreshing the grouping hierarchy after edits.
Editing Group Row Cells Copy Link
Double-click any group row's Sales cell to edit it. The new value is distributed equally among the group's children, rounding to integers. When a child is itself a group, the cascade recurses through the full hierarchy.
Set groupRowEditable on any column that should accept edits on group rows. This property accepts either a boolean or a callback; the callback is only invoked for nodes where rowNode.group === true, while leaf rows continue to honour editable.
When a column defines both groupRowEditable and editable, the grid evaluates only the property that matches the current node type, enabling separate rules for group rows and leaf rows.
When groupRowEditable is defined, the grid automatically distributes edited values to descendant rows using the built-in distribution. To customise this behaviour, set groupRowValueSetter to an options object, a custom callback function, or false to disable distribution.
Works like editable, but is evaluated only for group rows. When provided, group rows use
this property instead of editable. Set to true to make group row cells editable, or use
a callback to control editability per row.
When groupRowEditable is defined and no explicit groupRowValueSetter is provided,
the built-in distributeGroupValue is used automatically.
Columns with groupRowEditable or groupRowValueSetter do not require field or
valueSetter — the group row value setter handles the edit entirely.
Note: if groupRowValueSetter resolves to false or null (via distribution: false,
a per-aggFunc record entry, or groupRowValueSetter: false), the cell is treated as not
editable even when groupRowEditable is true. |
Controls how a group row value edit is distributed to descendant rows. true: Uses the built-in distributeGroupValue with default settings. groupRowEditable is defined and groupRowValueSetter is not set.false: Explicitly disables group row value distribution and makes the cell not editable, groupRowEditable is defined.GroupRowValueSetterParams and pushes field or valueSetter — the callback
handles the edit entirely.GroupRowValueSetterOptions distribution resolves to false or null for the column's aggFunc,
the cell is treated as not editable (overriding groupRowEditable).
Fires for every setDataValue call when active, regardless of groupRowEditable. |
Built-in Value Distribution Copy Link
The built-in distribution cascades the edited value to descendant rows, adjusting each child so the aggregate matches the new total. Pass an options object to groupRowValueSetter to customise the strategy or precision.
<ag-grid-angular
[columnDefs]="columnDefs"
/* other grid options ... */ />
this.columnDefs = [
{
field: 'sales',
aggFunc: 'sum',
editable: true,
groupRowEditable: true,
// Built-in distribution: divides the sum equally among children.
// precision: 0 rounds child values to integers and spreads the remainder.
groupRowValueSetter: { precision: 0 },
},
]; Distribution Strategies Copy Link
The distribution option controls how the edited value is spread across children. When omitted, the strategy is chosen automatically based on the column's aggFunc:
| Strategy | Behaviour | Default for |
|---|---|---|
'uniform' | Divides the new value equally among all children. | sum |
'overwrite' | Writes the new value directly to every child. | avg, no aggFunc |
'percentage' | Scales each child proportionally, preserving relative weights. Falls back to 'uniform' when the current total is zero. | — |
'increment' | Distributes only the difference (newValue − oldValue) among children, adding it to current values. | — |
Editing is disabled by default for count, min, max, first, last, and custom aggregation functions because there is no single correct way to split an edited value back into children for these functions. To enable editing, you can:
- Set
distributionto'overwrite'to write the new value directly to every child. - Provide a per-aggFunc record entry with
'overwrite',true, or a custom callback function that implements application-specific logic (see Per-Aggregation Strategies). - Assign a custom callback function to
groupRowValueSetterfor full control over the distribution.
<ag-grid-angular
[columnDefs]="columnDefs"
/* other grid options ... */ />
this.columnDefs = [
{
field: 'price',
aggFunc: 'min',
editable: true,
groupRowEditable: true,
// 'overwrite' writes the edited value to every child
groupRowValueSetter: { distribution: 'overwrite' },
},
{
field: 'quantity',
aggFunc: 'count',
editable: true,
groupRowEditable: true,
// Per-aggFunc record: enable only count with overwrite
groupRowValueSetter: { distribution: { count: 'overwrite' } },
},
];Setting distribution to true uses built-in defaults for sum, avg, no-aggFunc columns, and custom aggregation functions. The count/min/max/first/last functions remain disabled with true — use 'overwrite', a per-aggFunc record entry, or a custom callback to enable them.
Setting distribution to false or null suppresses distribution and makes the cell not editable, even when groupRowEditable is true.
For avg aggregation, the distributor adjusts child values so the average equals the edited value.
The precision option rounds the values written to child rows, not the group row's displayed value. The group value is re-computed by the column's aggFunc from the rounded children. For sum this is always exact. For avg the re-aggregated value may differ — for example, distributing precision: 0 across 3 children produces integers, but their average (10 / 3 ≈ 3.33) is not an integer. Use a Value Formatter to control how the group row displays the re-aggregated value.
Distribution Options Copy Link
Properties available on the GroupRowValueSetterOptions<TData = any, TValue = any, TContext = any> interface.
Distribution strategy or per-aggregation-function strategy map. As a string: applies the chosen GroupRowValueSetterDistribution strategy to all aggregation functions. As true: enables distribution using built-in defaults for distributable aggregation functions and custom aggFuncs (which get 'overwrite'). Note: count/min/max/first/last are only enabled via explicit per-aggFunc record entries. Useful for overriding false/null from defaultColDef in deep-merge scenarios. As false or null: suppresses distribution and makes the cell not editable (overriding groupRowEditable). As a record: maps aggFunc names to individual strategies, options objects, or custom callbacks. Unmatched aggFuncs fall through to default, then to the built-in defaults.
Example |
Fallback for aggFuncs that don't have a specific distribution strategy. When distribution is a record, applies to aggFuncs not listed in the record. When distribution is omitted, applies only to custom (non-built-in) aggFuncs. Ignored when distribution is a string (all aggFuncs use the specified strategy). Accepts the same values as record entries:'overwrite'). false or null to suppress distribution and make unmatched aggFunc cells not editable. Example |
Number of decimal places to round values written to child rows during distribution. Spreads any rounding remainder across children so their total matches exactly. 0 — integers (e.g. 10 / 3 → [4, 3, 3]) 2 — two decimals (e.g. 10 / 3 → [3.34, 3.33, 3.33]) false — disable rounding (overrides auto-detect) undefined (default) — auto-detect from the column definition: cellEditorParams.precision if set, 0 if cellEditorParams.step is a whole number, no rounding otherwise. Note: the group row's displayed value is re-computed by the aggFunc after distribution. For sum, the sum of rounded children always honours the same precision. For other aggregation functions like avg, the re-aggregated value may not — for example, the average of integers is not necessarily an integer. Ignored for bigint columns — bigint values are always distributed as integers.
Example |
Reads a child's current value during distribution. Default: node.getDataValue(column, 'value'). Override to read from a custom data structure or computed field.
Returns: The child's current value. Example |
Writes a distributed value to a child. Default: node.setDataValue(column, value, 'data'). Override to write to a custom data structure or apply transformations.
Returns: true if the value was changed, false otherwise.
Example |
Merging with Default Column Definitions Copy Link
When groupRowValueSetter is set as an options object on both defaultColDef and an individual column definition, the grid deep merges the two objects — the same way all column definition properties are merged. The column's own properties take precedence, and any properties not specified on the column are inherited from the default. This includes nested distribution records.
<ag-grid-angular
[defaultColDef]="defaultColDef"
[columnDefs]="columnDefs"
/* other grid options ... */ />
this.defaultColDef = {
editable: true,
groupRowEditable: true,
// Default: round child values to integers, disable count
groupRowValueSetter: { precision: 0, distribution: { count: false } },
};
this.columnDefs = [
{
field: 'sales',
aggFunc: 'sum',
// Column-level override: use percentage distribution.
// precision: 0 is inherited from defaultColDef.
groupRowValueSetter: { distribution: 'percentage' },
},
{
field: 'quantity',
aggFunc: 'count',
// Override defaultColDef's false with true to enable editing for count.
groupRowValueSetter: { distribution: { count: true } },
},
{
field: 'profit',
aggFunc: 'sum',
// No column-level override — inherits { precision: 0 } from defaultColDef.
},
];Deep merging only applies when both the defaultColDef and the column define groupRowValueSetter as plain objects. If the column defines a callback function, it replaces the default entirely.
Multiple Aggregation Functions Copy Link
The built-in distribution handles standard aggregation functions. The following example demonstrates a mix of built-in strategies, non-aggregated columns, and custom callbacks. Double-click any group row cell to edit it:
- Salary (
sum): divides the new total equally among children, rounding to integers (precision: 0). For example, editing a group total of 300 across 3 children produces 100 each. - Bonus (
avg): overwrites every child with the edited value (the default foravg). For example, editing the group average to 15 sets all children's bonus to 15. - Projects (no
aggFunc): overwrites every child with the edited value (the default when there is no aggregation). The group cell is blank but still editable. - Rate % (custom callback): reads each leaf child's salary and computes their bonus as
salary × rate / 100. For example, entering 20 sets each child's bonus to 20% of their individual salary. UsesallLeafChildrento reach all descendant data rows directly. - SumSq (custom
sumOfSquaresaggFunc): uses a custom callback that computes√(newValue / count)per child — the correct inverse of sum-of-squares.
Per-Aggregation Strategies Copy Link
When different columns use different aggregation functions but share a groupRowValueSetter (e.g. via defaultColDef), the distribution property can be set to a record mapping each aggregation function name to a strategy string, an options object, a custom callback function, true to enable with built-in defaults, or false/null to suppress distribution for that aggFunc:
<ag-grid-angular
[aggFuncs]="aggFuncs"
[defaultColDef]="defaultColDef"
/* other grid options ... */ />
const myCustomAgg = (params) => {
let total = 0;
for (const value of params.values) {
total += Number(value) || 0;
}
return total;
};
// Optional: inverse enables incremental aggregation on row changes
myCustomAgg.inverse = (params) => {
let total = Number(params.result) || 0;
for (const value of params.values) {
total -= Number(value) || 0;
}
return total;
};
this.aggFuncs = {
myCustomAgg,
};
this.defaultColDef = {
editable: true,
groupRowEditable: true,
groupRowValueSetter: {
precision: 0,
distribution: {
sum: 'percentage', // strategy string
avg: { distribution: 'increment' }, // options object
myCustomAgg: (params) => { // custom callback function
for (const child of params.aggregatedChildren) {
child.setDataValue(params.column, params.newValue, 'data');
}
return true;
},
},
},
};Custom aggregation functions are disabled by default. To enable editing, add an explicit entry in the distribution record — either a strategy string, an options object, or a custom callback function that implements the inverse of the aggregation logic:
<ag-grid-angular
[defaultColDef]="defaultColDef"
/* other grid options ... */ />
this.defaultColDef = {
editable: true,
groupRowEditable: true,
groupRowValueSetter: {
precision: 0,
distribution: {
sum: 'percentage',
avg: 'increment',
count: 'overwrite',
// Custom aggFunc: provide a callback that reverses the aggregation
myCustomAgg: (params) => {
for (const child of params.aggregatedChildren) {
child.setDataValue(params.column, params.newValue, 'data');
}
return true;
},
},
},
};Aggregation functions not listed in the record fall through to the default fallback, then to the built-in defaults (see the strategy table above).
Default Fallback Copy Link
When multiple custom aggregation functions share the same distribution logic, the default property avoids repeating entries for each one. It applies to any aggregation function not explicitly listed in the distribution record — including custom aggregation functions (which are disabled without it) and columns without an aggFunc (which already default to 'overwrite' via built-in defaults). Non-distributable functions (count, min, max, first, last) are not affected by default and must always be listed explicitly.
<ag-grid-angular
[defaultColDef]="defaultColDef"
/* other grid options ... */ />
this.defaultColDef = {
editable: true,
groupRowEditable: true,
groupRowValueSetter: {
precision: 0,
distribution: {
sum: 'percentage',
count: 'overwrite',
},
// Fallback for unlisted custom aggFuncs and no-aggFunc columns.
default: 'overwrite',
},
}; Custom Distribution with a Callback Copy Link
For full control over how group-level edits cascade, assign a function to groupRowValueSetter. The callback receives a GroupRowValueSetterParams object and should return true if any child value was changed.
The following example demonstrates a custom function that distributes the edited total equally among children, rounding child values to integers (largest remainder method). The function calls setDataValue on each child, which triggers aggregation to refresh parent totals automatically.
GroupRowValueSetterParams Copy Link
Properties available on the GroupRowValueSetterParams<TData = any, TValue = any, TContext = any> interface.
The grid api. |
Application context as set on gridOptions.context. |
Column for this callback. |
ColDef provided for this column. |
The value before the change. |
The value after the change. |
The group row node being edited. |
Row data for the group node. null or undefined for grouping groups or tree data filler nodes. |
What triggered the edit (e.g. 'ui', 'undo', 'paste'). |
Whether the aggregated value actually changed compared to the previous value. |
The immediate children that contribute to this group's aggregation. setDataValue() on a child group cascades the edit recursively through the full hierarchy. The built-in distributeGroupValue does this automatically.rowNode.getAggregatedChildren(colKey) to retrieve the same children programmatically. Pass true as the second argument to collect all descendant leaf rows recursively. Only supported with the Client-Side Row Model.
|
Aggregated Children Copy Link
The groupRowValueSetter callback receives an aggregatedChildren array containing the immediate children that contribute to the Aggregation for the edited column. This array makes it easy to cascade edited values down to descendant rows without manually traversing the row hierarchy.
The children returned by aggregatedChildren depend on the column and grid configuration:
- Regular value columns: Returns direct children used for aggregation. This respects the
suppressAggFilteredOnlygrid option andgroupAggFiltering— when these cause aggregation to include all children (not just filtered ones),aggregatedChildrenreturns all children accordingly. - Pivot columns on leaf groups: Returns only the children matching the column's pivot keys, since pivot aggregation groups rows by pivot key values.
- Non-group rows: Always returns an empty array — only group rows have aggregated children.
Alternatively, rowNode.getAggregatedChildren(colKey) can be called anywhere in code to retrieve the same children programmatically. To collect all descendant leaf rows instead of only the immediate children, pass true as the second argument: rowNode.getAggregatedChildren(colKey, true).
See Retrieving Aggregated Children for more details.
Cascading Edits Copy Link
Key points for implementing cascading edits:
- Calling
rowNode.setDataValueon each child pushes updated figures down. - When a child is also a group that defines its own
groupRowValueSetter, the cascade recurses further. - Parent aggregates refresh automatically because child
datachanges re-run the columnaggFunc, so editing a group row total instantly rebalances the children to reach that value. - Aggregation is batched automatically — all
setDataValuecalls within a singlegroupRowValueSetterinvocation are processed before re-aggregation runs.
The aggregatedChildren parameter and rowNode.getAggregatedChildren() method are only supported with the Client-Side Row Model. For other row models, aggregatedChildren is an empty array.
Calling distributeGroupValue Copy Link
The built-in distribution function distributeGroupValue is exported from ag-grid-enterprise and can be called directly inside a custom groupRowValueSetter callback. This is useful when custom logic is needed before or after the distribution:
import { distributeGroupValue } from 'ag-grid-enterprise';
colDef.groupRowValueSetter = (params) => {
// Custom pre-processing
const adjusted = Math.max(0, Number(params.newValue));
return distributeGroupValue({ ...params, newValue: adjusted }, { distribution: 'percentage' });
};
You can also assign distributeGroupValue directly without a wrapper for default behaviour:
import { distributeGroupValue } from 'ag-grid-enterprise';
colDef.groupRowValueSetter = distributeGroupValue;
Custom getValue and setValue Copy Link
The getValue and setValue callbacks let you override how the built-in distributor reads from and writes to child rows. By default, the distributor uses node.getDataValue(column, 'value') to read and node.setDataValue(column, value, 'data') to write.
This is useful when:
- Child values are stored in a different field or custom data structure.
- You need to apply transformations or side effects during distribution.
- The column's
valueSetterorvalueGetterdon't match the distribution needs.
<ag-grid-angular
[columnDefs]="columnDefs"
/* other grid options ... */ />
this.columnDefs = [
{
field: 'amount',
aggFunc: 'sum',
editable: true,
groupRowEditable: true,
groupRowValueSetter: {
distribution: 'percentage',
getValue: (params) => (params.data && params.data.weight) || 0,
setValue: (params) => params.node.setDataValue(params.column, params.value, 'data'),
},
},
]; Editing Pivot Columns Copy Link
When using Pivoting, the groupRowValueSetter also works with pivot result columns. The key difference is that aggregatedChildren returns only the children matching the column's pivot keys, not all children of the group.
The following example uses the built-in distribution with pivot mode. Since groupRowEditable is defined, the distribution is enabled automatically. Double-click any pivot cell to edit it. The edited value is distributed uniformly among the children matching that pivot column's keys.
For full control over pivot editing, use a custom groupRowValueSetter callback. In the following example:
- The grid is pivoted by
product(Electronics, Clothing, Food), grouped byregionandcountry. - Double-clicking on any pivot column cell opens the editor for the aggregated value.
- When editing a pivot cell (e.g., "Electronics" column on "Europe" row), the
aggregatedChildrenarray contains only the European rows selling Electronics — not all European rows. - The edit is distributed equally among just those matching children, leaving other product categories unchanged.
This behaviour ensures that edits on pivot columns affect only the data that contributes to that specific pivot value, making cascading edits work correctly with pivoted data.
Tree Data Copy Link
The groupRowEditable and groupRowValueSetter features also work with Tree Data. In tree data mode, parent nodes act as group rows — editing a parent's aggregated value cascades the change down to its children, just like with row grouping.
The following example shows a company's budget organised as a tree: departments contain teams, and teams contain employees. Editing a department's or team's budget distributes the new total equally among its members, rounding to integers. The cascade recurses through the full hierarchy automatically.
Refreshing Groups After Editing Copy Link
When grouped columns are editable, setting refreshAfterGroupEdit=true causes the grid to update row data and recalculate the grouping after every committed edit. Without this option, the row data updates but the grouping does not get updated until the next full refresh.
When enabling refreshAfterGroupEdit, also provide getRowId so that the grid can track rows by stable IDs while rebuilding the grouping hierarchy.
The following example demonstrates this behaviour. Double-click on a department or team cell to edit it — the grid re-evaluates the grouping and moves the row to the correct group instantly.
See also Read Only Edit for configuring immutable grouped data or connecting the grid with a store.