Grouping & Summaries
Drag a column header into the panel to group; click a group row to collapse it. Group and total aggregates come from the groupSummary / totalSummary column inputs. The Id column is pinned left; the column chooser hides columns.
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { OgeColumn, OgeGrid } from '@oge-ui/grid';
@Component({
selector: 'demo-root',
imports: [OgeColumn, OgeGrid],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<oge-grid [data]="employees" keyField="id"
[groupPanel]="true" [groupBy]="['department']" [columnChooser]="true">
<oge-column field="id" caption="Id" [width]="80" dataType="number" pinned="left" />
<oge-column field="firstName" caption="First Name" />
<oge-column field="salary" caption="Salary" dataType="number"
groupSummary="avg" totalSummary="sum" [format]="money" />
</oge-grid>
`,
})
export class Demo {
readonly employees = [
{ id: 1, firstName: 'Ali', lastName: 'Yılmaz', department: 'Engineering', city: 'İstanbul', salary: 8400, hireDate: '2019-03-11' },
{ id: 2, firstName: 'Ayşe', lastName: 'Kaya', department: 'Sales', city: 'Ankara', salary: 7200, hireDate: '2020-07-02' },
{ id: 3, firstName: 'Mehmet', lastName: 'Demir', department: 'Engineering', city: 'İzmir', salary: 9100, hireDate: '2018-01-23' },
{ id: 4, firstName: 'Zeynep', lastName: 'Şahin', department: 'Finance', city: 'İstanbul', salary: 6800, hireDate: '2021-11-15' },
{ id: 5, firstName: 'Emre', lastName: 'Çelik', department: 'Support', city: 'Bursa', salary: 5400, hireDate: '2022-05-09' },
];
protected readonly money = (value: unknown): string =>
Number(value).toLocaleString('de-DE', { style: 'currency', currency: 'EUR' });
}Multiple, custom and group-footer summaries
A column may declare a list of aggregates, place them on a dedicated footer row after each group (groupSummaryPosition="footer"), or compute its own value with calculateCustomSummary — here the City column counts distinct cities per department.
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { OgeColumn, OgeGrid } from '@oge-ui/grid';
interface Employee {
city: string;
salary: number;
}
@Component({
selector: 'demo-root',
imports: [OgeColumn, OgeGrid],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<oge-grid [data]="employees" keyField="id" [groupBy]="['department']">
<!-- custom aggregate: any reducer over the group's rows -->
<oge-column field="city" groupSummary="custom"
[calculateCustomSummary]="distinctCities" />
<!-- several aggregates at once, rendered on a footer row after the group -->
<oge-column field="salary" dataType="number"
[groupSummary]="['min', 'max']" groupSummaryPosition="footer"
[totalSummary]="['sum', 'avg']" />
</oge-grid>
`,
})
export class Demo {
readonly employees = [
{ id: 1, firstName: 'Ali', lastName: 'Yılmaz', department: 'Engineering', city: 'İstanbul', salary: 8400, hireDate: '2019-03-11' },
{ id: 2, firstName: 'Ayşe', lastName: 'Kaya', department: 'Sales', city: 'Ankara', salary: 7200, hireDate: '2020-07-02' },
{ id: 3, firstName: 'Mehmet', lastName: 'Demir', department: 'Engineering', city: 'İzmir', salary: 9100, hireDate: '2018-01-23' },
{ id: 4, firstName: 'Zeynep', lastName: 'Şahin', department: 'Finance', city: 'İstanbul', salary: 6800, hireDate: '2021-11-15' },
{ id: 5, firstName: 'Emre', lastName: 'Çelik', department: 'Support', city: 'Bursa', salary: 5400, hireDate: '2022-05-09' },
];
protected readonly distinctCities = (rows: readonly Employee[]): string =>
`${new Set(rows.map((r) => r.city)).size} cities`;
}Deferred group loading
With grouping.autoExpandAll: false groups start collapsed, and a remote source may answer grouped requests with headers only (items: null plus a count). A group's rows are fetched the moment it is expanded — filtered server-side by the group value — and cached for later toggles. Expand a department below and watch the skeleton row while its request is in flight.
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { OgeColumn, OgeGrid } from '@oge-ui/grid';
import { CustomDataSource } from '@oge-ui/core';
interface Employee {
id: number;
firstName: string;
lastName: string;
city: string;
department: string;
salary: number;
}
@Component({
selector: 'demo-root',
imports: [OgeColumn, OgeGrid],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<oge-grid [data]="source" keyField="id"
[groupBy]="['department']"
[grouping]="{ autoExpandAll: false }">
<oge-column field="firstName" caption="First Name" />
<oge-column field="lastName" caption="Last Name" />
<oge-column field="city" caption="City" />
<oge-column field="salary" caption="Salary" dataType="number" />
</oge-grid>
`,
})
export class Demo {
// grouped request → headers only (items: null, count);
// expanding a group fetches its rows with an eq-filter
protected readonly source = new CustomDataSource<Employee>({
key: 'id',
load: async (options) => {
if (options.group?.length) {
const groups = await fetch('/api/employees/groups?by=department');
return { data: await groups.json() }; // [{ key, items: null, count }]
}
const rows = await fetch(
'/api/employees?' + JSON.stringify(options.filter ?? null),
);
return { data: await rows.json() };
},
});
}Notes
- Group aggregates (
sum · avg · min · max · count) come from the column'sgroupSummaryinput;totalSummaryfeeds the sticky totals row at the bottom. - Expanding/collapsing a group is pure client-side state — it never triggers a data reload.
- With a remote DataSource the grid sends
groupdescriptors and expects the standard nestedGroupedItempayload — local and server grouping render identically. - Column headers can also be dragged onto each other to reorder, and resized from their edges.