igniteui-angular 21.1.0-rc.4 → 21.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -0
- package/fesm2022/igniteui-angular-grids-core.mjs +5 -3
- package/fesm2022/igniteui-angular-grids-core.mjs.map +1 -1
- package/fesm2022/igniteui-angular-grids-grid.mjs +6 -1
- package/fesm2022/igniteui-angular-grids-grid.mjs.map +1 -1
- package/fesm2022/igniteui-angular-grids-hierarchical-grid.mjs +1 -0
- package/fesm2022/igniteui-angular-grids-hierarchical-grid.mjs.map +1 -1
- package/fesm2022/igniteui-angular-grids-lite.mjs +2 -0
- package/fesm2022/igniteui-angular-grids-lite.mjs.map +1 -1
- package/fesm2022/igniteui-angular-simple-combo.mjs +1 -1
- package/fesm2022/igniteui-angular-simple-combo.mjs.map +1 -1
- package/lib/core/styles/components/grid/_grid-theme.scss +4 -1
- package/migrations/migration-collection.json +7 -0
- package/migrations/update-21_1_0_add-agent-skills/index.d.ts +3 -0
- package/migrations/update-21_1_0_add-agent-skills/index.js +46 -0
- package/package.json +4 -4
- package/skills/igniteui-angular-components/SKILL.md +71 -0
- package/skills/igniteui-angular-components/references/charts.md +447 -0
- package/skills/igniteui-angular-components/references/data-display.md +347 -0
- package/skills/igniteui-angular-components/references/directives.md +149 -0
- package/skills/igniteui-angular-components/references/feedback.md +141 -0
- package/skills/igniteui-angular-components/references/form-controls.md +298 -0
- package/skills/igniteui-angular-components/references/layout-manager.md +415 -0
- package/skills/igniteui-angular-components/references/layout.md +216 -0
- package/skills/igniteui-angular-components/references/setup.md +157 -0
- package/skills/igniteui-angular-grids/SKILL.md +110 -0
- package/skills/igniteui-angular-grids/references/data-operations.md +436 -0
- package/skills/igniteui-angular-grids/references/editing.md +480 -0
- package/skills/igniteui-angular-grids/references/features.md +218 -0
- package/skills/igniteui-angular-grids/references/paging-remote.md +388 -0
- package/skills/igniteui-angular-grids/references/state.md +448 -0
- package/skills/igniteui-angular-grids/references/structure.md +290 -0
- package/skills/igniteui-angular-grids/references/types.md +428 -0
- package/skills/igniteui-angular-theming/SKILL.md +530 -0
- package/styles/igniteui-angular-dark.css +1 -1
- package/styles/igniteui-angular.css +1 -1
- package/styles/igniteui-bootstrap-dark.css +1 -1
- package/styles/igniteui-bootstrap-light.css +1 -1
- package/styles/igniteui-dark-green.css +1 -1
- package/styles/igniteui-fluent-dark-excel.css +1 -1
- package/styles/igniteui-fluent-dark-word.css +1 -1
- package/styles/igniteui-fluent-dark.css +1 -1
- package/styles/igniteui-fluent-light-excel.css +1 -1
- package/styles/igniteui-fluent-light-word.css +1 -1
- package/styles/igniteui-fluent-light.css +1 -1
- package/styles/igniteui-indigo-dark.css +1 -1
- package/styles/igniteui-indigo-light.css +1 -1
- package/styles/maps/igniteui-angular-dark.css.map +1 -1
- package/styles/maps/igniteui-angular.css.map +1 -1
- package/styles/maps/igniteui-bootstrap-dark.css.map +1 -1
- package/styles/maps/igniteui-bootstrap-light.css.map +1 -1
- package/styles/maps/igniteui-dark-green.css.map +1 -1
- package/styles/maps/igniteui-fluent-dark-excel.css.map +1 -1
- package/styles/maps/igniteui-fluent-dark-word.css.map +1 -1
- package/styles/maps/igniteui-fluent-dark.css.map +1 -1
- package/styles/maps/igniteui-fluent-light-excel.css.map +1 -1
- package/styles/maps/igniteui-fluent-light-word.css.map +1 -1
- package/styles/maps/igniteui-fluent-light.css.map +1 -1
- package/styles/maps/igniteui-indigo-dark.css.map +1 -1
- package/styles/maps/igniteui-indigo-light.css.map +1 -1
- package/types/igniteui-angular-grids-lite.d.ts +2 -0
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
# Grid Editing — Cell Editing, Row Editing, Batch Editing & Validation
|
|
2
|
+
|
|
3
|
+
> **Part of the [`igniteui-angular-grids`](../SKILL.md) skill hub.**
|
|
4
|
+
> For grid import patterns and `viewChild` access — see [`data-operations.md`](./data-operations.md).
|
|
5
|
+
> For state persistence — see [`state.md`](./state.md).
|
|
6
|
+
> For paging and remote data — see [`paging-remote.md`](./paging-remote.md).
|
|
7
|
+
|
|
8
|
+
## Editing Data Through the Grid
|
|
9
|
+
|
|
10
|
+
> **AGENT INSTRUCTION:** When a user says they want to "edit data through the grid", "make the grid editable", or "allow CRUD in the grid", use this section to pick the right editing mode before writing any code.
|
|
11
|
+
|
|
12
|
+
### Choosing an Editing Mode
|
|
13
|
+
|
|
14
|
+
| Mode | When to use | Key properties |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| **Cell editing** | Each cell saves immediately when the user confirms or leaves it. Good for quick single-field corrections. | `[editable]="true"` on columns + `(cellEditDone)` |
|
|
17
|
+
| **Row editing** | User edits multiple cells in a row and confirms/cancels the whole row at once. **Best for most CRUD UIs.** | `[rowEditable]="true"` + `[editable]="true"` on columns + `(rowEditDone)` |
|
|
18
|
+
| **Batch editing** | Accumulate many changes across multiple rows with undo/redo, then commit or discard all at once. | `[batchEditing]="true"` + `[rowEditable]="true"` |
|
|
19
|
+
|
|
20
|
+
> **Default recommendation:** use **row editing** for most data management UIs (e.g., "edit available cars"). It prevents half-edited data from being visible and gives users a clear Done/Cancel flow per row.
|
|
21
|
+
|
|
22
|
+
### Cell Editing (Immediate)
|
|
23
|
+
|
|
24
|
+
> **Docs:** [Cell Editing](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/cell-editing)
|
|
25
|
+
|
|
26
|
+
The simplest mode. Each cell saves the moment the user tabs away or presses Enter.
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { Component, ChangeDetectionStrategy, signal, viewChild, inject } from '@angular/core';
|
|
30
|
+
import { IgxGridComponent, IGX_GRID_DIRECTIVES } from 'igniteui-angular/grids/grid';
|
|
31
|
+
import { IGridEditDoneEventArgs } from 'igniteui-angular/grids/core';
|
|
32
|
+
|
|
33
|
+
@Component({
|
|
34
|
+
selector: 'app-cars-grid',
|
|
35
|
+
imports: [IGX_GRID_DIRECTIVES],
|
|
36
|
+
templateUrl: './cars-grid.component.html',
|
|
37
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
38
|
+
})
|
|
39
|
+
export class CarsGridComponent {
|
|
40
|
+
gridRef = viewChild.required<IgxGridComponent>('grid');
|
|
41
|
+
private carService = inject(CarService);
|
|
42
|
+
protected cars = signal<Car[]>([]);
|
|
43
|
+
|
|
44
|
+
constructor() {
|
|
45
|
+
this.carService.getCars().subscribe(data => this.cars.set(data));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
onCellEditDone(event: IGridEditDoneEventArgs) {
|
|
49
|
+
// Persist the single-cell change immediately
|
|
50
|
+
const updatedCar = { ...event.rowData, [event.column.field]: event.newValue };
|
|
51
|
+
this.carService.updateCar(updatedCar).subscribe();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```html
|
|
57
|
+
<igx-grid #grid
|
|
58
|
+
[data]="cars()"
|
|
59
|
+
[primaryKey]="'id'"
|
|
60
|
+
[autoGenerate]="false"
|
|
61
|
+
(cellEditDone)="onCellEditDone($event)"
|
|
62
|
+
height="600px">
|
|
63
|
+
<igx-column field="make" header="Make" [editable]="true" [sortable]="true"></igx-column>
|
|
64
|
+
<igx-column field="model" header="Model" [editable]="true" [sortable]="true"></igx-column>
|
|
65
|
+
<igx-column field="year" header="Year" dataType="number" [editable]="true" [sortable]="true"></igx-column>
|
|
66
|
+
<igx-column field="price" header="Price" dataType="number" [editable]="true" [sortable]="true"></igx-column>
|
|
67
|
+
<igx-column field="available" header="Available" dataType="boolean" [editable]="true"></igx-column>
|
|
68
|
+
</igx-grid>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Row Editing (Recommended for CRUD)
|
|
72
|
+
|
|
73
|
+
> **Docs:** [Row Editing](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/row-editing)
|
|
74
|
+
|
|
75
|
+
Users click into a row, edit cells, then click **Done** or **Cancel** — changes only apply when Done is pressed. An overlay toolbar appears automatically.
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { Component, ChangeDetectionStrategy, signal, viewChild, inject } from '@angular/core';
|
|
79
|
+
import { IgxGridComponent, IGX_GRID_DIRECTIVES } from 'igniteui-angular/grids/grid';
|
|
80
|
+
import { IGridEditDoneEventArgs, IGridEditEventArgs, IRowDataEventArgs } from 'igniteui-angular/grids/core';
|
|
81
|
+
|
|
82
|
+
@Component({
|
|
83
|
+
selector: 'app-cars-grid',
|
|
84
|
+
imports: [IGX_GRID_DIRECTIVES],
|
|
85
|
+
templateUrl: './cars-grid.component.html',
|
|
86
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
87
|
+
})
|
|
88
|
+
export class CarsGridComponent {
|
|
89
|
+
gridRef = viewChild.required<IgxGridComponent>('grid');
|
|
90
|
+
private carService = inject(CarService);
|
|
91
|
+
protected cars = signal<Car[]>([]);
|
|
92
|
+
|
|
93
|
+
constructor() {
|
|
94
|
+
this.carService.getCars().subscribe(data => this.cars.set(data));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
onRowEditDone(event: IGridEditDoneEventArgs) {
|
|
98
|
+
// event.newValue contains the full updated row object
|
|
99
|
+
this.carService.updateCar(event.newValue).subscribe();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
onRowAdded(event: IRowDataEventArgs) {
|
|
103
|
+
// Persist the newly added row; optionally replace local data with server response
|
|
104
|
+
this.carService.createCar(event.data).subscribe(created => {
|
|
105
|
+
this.cars.update(cars => cars.map(c => c === event.data ? created : c));
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
onRowDeleted(event: IRowDataEventArgs) {
|
|
110
|
+
this.carService.deleteCar(event.data.id).subscribe();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
addCar() {
|
|
114
|
+
// Programmatically start a new row at the end
|
|
115
|
+
this.gridRef().beginAddRowByIndex(this.cars().length);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
deleteCar(carId: number) {
|
|
119
|
+
this.gridRef().deleteRow(carId);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
```html
|
|
125
|
+
<igx-grid #grid
|
|
126
|
+
[data]="cars()"
|
|
127
|
+
[primaryKey]="'id'"
|
|
128
|
+
[autoGenerate]="false"
|
|
129
|
+
[rowEditable]="true"
|
|
130
|
+
(rowEditDone)="onRowEditDone($event)"
|
|
131
|
+
(rowAdded)="onRowAdded($event)"
|
|
132
|
+
(rowDeleted)="onRowDeleted($event)"
|
|
133
|
+
height="600px">
|
|
134
|
+
|
|
135
|
+
<igx-column field="make" header="Make" [editable]="true" [sortable]="true"></igx-column>
|
|
136
|
+
<igx-column field="model" header="Model" [editable]="true" [sortable]="true"></igx-column>
|
|
137
|
+
<igx-column field="year" header="Year" dataType="number" [editable]="true" [sortable]="true"></igx-column>
|
|
138
|
+
<igx-column field="price" header="Price" dataType="number" [editable]="true" [sortable]="true"></igx-column>
|
|
139
|
+
<igx-column field="available" header="Available" dataType="boolean" [editable]="true"></igx-column>
|
|
140
|
+
|
|
141
|
+
<!-- Action strip: shows Edit and Delete buttons on row hover; Add Row button in toolbar -->
|
|
142
|
+
<igx-action-strip>
|
|
143
|
+
<igx-grid-editing-actions [addRow]="true"></igx-grid-editing-actions>
|
|
144
|
+
</igx-action-strip>
|
|
145
|
+
</igx-grid>
|
|
146
|
+
|
|
147
|
+
<button (click)="addCar()">Add Car</button>
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
> **Key inputs summary:**
|
|
151
|
+
> - `[rowEditable]="true"` — enables the Done/Cancel overlay per row
|
|
152
|
+
> - `[editable]="true"` on each `igx-column` — marks which fields the user can change
|
|
153
|
+
> - `[primaryKey]` — **required** for editing to work
|
|
154
|
+
> - `[autoGenerate]="false"` — always define columns explicitly when editing is enabled so you control which fields are editable
|
|
155
|
+
> - `<igx-action-strip>` with `<igx-grid-editing-actions>` — adds hover Edit/Delete buttons and an optional Add Row button automatically
|
|
156
|
+
|
|
157
|
+
### Programmatic Row Adding with Default Values
|
|
158
|
+
|
|
159
|
+
When starting a new row programmatically, pre-populate fields using `(cellEditEnter)` on the new row:
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
onCellEditEnter(event: IGridEditEventArgs) {
|
|
163
|
+
if (event.isAddRow && event.column.field === 'available') {
|
|
164
|
+
event.cellEditArgs.newValue = true; // default new cars to available
|
|
165
|
+
}
|
|
166
|
+
if (event.isAddRow && event.column.field === 'year') {
|
|
167
|
+
event.cellEditArgs.newValue = new Date().getFullYear();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
```html
|
|
173
|
+
<igx-grid #grid ... (cellEditEnter)="onCellEditEnter($event)">
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Batch Editing & Transactions
|
|
179
|
+
|
|
180
|
+
> **Docs:** [Batch Editing](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/batch-editing) (substitute URL prefix per grid type)
|
|
181
|
+
|
|
182
|
+
> **Applies to**: Flat Grid, Tree Grid, and Hierarchical Grid. **Pivot Grid does NOT support batch editing.**
|
|
183
|
+
> Use batch editing when users need to **edit many rows at once** and commit or discard all changes together, with undo/redo support.
|
|
184
|
+
|
|
185
|
+
### Enabling Batch Editing
|
|
186
|
+
|
|
187
|
+
```html
|
|
188
|
+
<igx-grid #grid
|
|
189
|
+
[data]="data()"
|
|
190
|
+
[primaryKey]="'id'"
|
|
191
|
+
[batchEditing]="true"
|
|
192
|
+
[rowEditable]="true"
|
|
193
|
+
height="600px">
|
|
194
|
+
<igx-column field="name" [editable]="true"></igx-column>
|
|
195
|
+
<igx-column field="price" dataType="number" [editable]="true"></igx-column>
|
|
196
|
+
<igx-column field="quantity" dataType="number" [editable]="true"></igx-column>
|
|
197
|
+
</igx-grid>
|
|
198
|
+
|
|
199
|
+
<button (click)="commitChanges()">Save All</button>
|
|
200
|
+
<button (click)="undoLast()">Undo</button>
|
|
201
|
+
<button (click)="redoLast()">Redo</button>
|
|
202
|
+
<button (click)="discardAll()">Discard</button>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Managing Transactions
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
commitChanges() {
|
|
209
|
+
// Exit any active edit before committing
|
|
210
|
+
this.gridRef().endEdit(true);
|
|
211
|
+
this.gridRef().transactions.commit(this.gridRef().data);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
undoLast() {
|
|
215
|
+
// Must exit edit mode before undo/redo
|
|
216
|
+
this.gridRef().endEdit(true);
|
|
217
|
+
this.gridRef().transactions.undo();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
redoLast() {
|
|
221
|
+
this.gridRef().endEdit(true);
|
|
222
|
+
this.gridRef().transactions.redo();
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
discardAll() {
|
|
226
|
+
this.gridRef().endEdit(false);
|
|
227
|
+
this.gridRef().transactions.clear();
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Use `canUndo` and `canRedo` to control button state:
|
|
232
|
+
|
|
233
|
+
```html
|
|
234
|
+
<button (click)="commitChanges()" [disabled]="gridRef().transactions.getAggregatedChanges(false).length < 1">Save All</button>
|
|
235
|
+
<button (click)="undoLast()" [disabled]="!gridRef().transactions.canUndo">Undo</button>
|
|
236
|
+
<button (click)="redoLast()" [disabled]="!gridRef().transactions.canRedo">Redo</button>
|
|
237
|
+
<button (click)="discardAll()">Discard</button>
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Transaction State
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
// Check if there are pending changes
|
|
244
|
+
const hasPendingChanges = this.gridRef().transactions.getAggregatedChanges(false).length > 0;
|
|
245
|
+
|
|
246
|
+
// Get all pending transactions
|
|
247
|
+
const pending = this.gridRef().transactions.getAggregatedChanges(true);
|
|
248
|
+
// Each transaction has: { id, type ('add'|'update'|'delete'), newValue }
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Sending Batch Changes to Server
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
saveToServer() {
|
|
255
|
+
const changes = this.gridRef().transactions.getAggregatedChanges(true);
|
|
256
|
+
|
|
257
|
+
const adds = changes.filter(t => t.type === 'add').map(t => t.newValue);
|
|
258
|
+
const updates = changes.filter(t => t.type === 'update').map(t => ({ id: t.id, ...t.newValue }));
|
|
259
|
+
const deletes = changes.filter(t => t.type === 'delete').map(t => t.id);
|
|
260
|
+
|
|
261
|
+
this.dataService.saveBatch({ adds, updates, deletes }).subscribe(() => {
|
|
262
|
+
this.gridRef().transactions.commit(this.gridRef().data);
|
|
263
|
+
this.gridRef().transactions.clear();
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Excel-Style Editing Workflows
|
|
269
|
+
|
|
270
|
+
### Inline Cell Editing with Validation
|
|
271
|
+
|
|
272
|
+
```html
|
|
273
|
+
<igx-grid #grid
|
|
274
|
+
[data]="data()"
|
|
275
|
+
[primaryKey]="'id'"
|
|
276
|
+
[batchEditing]="true"
|
|
277
|
+
(cellEditDone)="onCellEditDone($event)">
|
|
278
|
+
|
|
279
|
+
<igx-column field="name" [editable]="true" required></igx-column>
|
|
280
|
+
<igx-column field="email" [editable]="true" [validators]="emailValidators"></igx-column>
|
|
281
|
+
<igx-column field="quantity" dataType="number" [editable]="true" [validators]="quantityValidators"></igx-column>
|
|
282
|
+
</igx-grid>
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
import { Validators } from '@angular/forms';
|
|
287
|
+
|
|
288
|
+
emailValidators = [Validators.required, Validators.email];
|
|
289
|
+
quantityValidators = [Validators.required, Validators.min(0), Validators.max(9999)];
|
|
290
|
+
|
|
291
|
+
onCellEditDone(event: IGridEditDoneEventArgs) {
|
|
292
|
+
// React to edits — e.g., recalculate totals
|
|
293
|
+
if (event.column.field === 'quantity' || event.column.field === 'unitPrice') {
|
|
294
|
+
this.recalculateRowTotal(event.rowID);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Clipboard Paste for Bulk Edit
|
|
300
|
+
|
|
301
|
+
Grids support paste from Excel/spreadsheets by default. Configure clipboard behavior:
|
|
302
|
+
|
|
303
|
+
```html
|
|
304
|
+
<igx-grid #grid
|
|
305
|
+
[data]="data()"
|
|
306
|
+
[primaryKey]="'id'"
|
|
307
|
+
[batchEditing]="true"
|
|
308
|
+
[clipboardOptions]="{ enabled: true, copyHeaders: true, copyFormatters: true, separator: '\t' }">
|
|
309
|
+
</igx-grid>
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Row Adding via UI
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
// Flat Grid / Hierarchical Grid:
|
|
316
|
+
this.gridRef().beginAddRowByIndex(0); // at top
|
|
317
|
+
this.gridRef().beginAddRowById('ALFKI'); // under a specific row
|
|
318
|
+
this.gridRef().beginAddRowByIndex(this.gridRef().data.length); // at end
|
|
319
|
+
|
|
320
|
+
// Tree Grid — add as child of a parent:
|
|
321
|
+
this.treeGridRef().addRow(newRowData, parentRowID); // add row as child of parentRowID
|
|
322
|
+
this.treeGridRef().beginAddRowByIndex(3, true); // add as child of row at index 3
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
Use with Action Strip for visual add/edit actions:
|
|
326
|
+
|
|
327
|
+
```html
|
|
328
|
+
<igx-grid #grid [data]="data()" [primaryKey]="'id'" [rowEditable]="true">
|
|
329
|
+
<igx-action-strip>
|
|
330
|
+
<igx-grid-editing-actions [addRow]="true"></igx-grid-editing-actions>
|
|
331
|
+
</igx-action-strip>
|
|
332
|
+
<igx-column field="name" [editable]="true"></igx-column>
|
|
333
|
+
</igx-grid>
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
## Editing Events Reference
|
|
337
|
+
|
|
338
|
+
All grids fire a consistent sequence of events during cell and row editing:
|
|
339
|
+
|
|
340
|
+
| Event | Fires When | Cancelable |
|
|
341
|
+
|---|---|---|
|
|
342
|
+
| `(rowEditEnter)` | Row enters edit mode | Yes |
|
|
343
|
+
| `(cellEditEnter)` | Cell enters edit mode (after `rowEditEnter`) | Yes |
|
|
344
|
+
| `(cellEdit)` | Cell value is about to be committed | Yes |
|
|
345
|
+
| `(cellEditDone)` | Cell value has been committed | No |
|
|
346
|
+
| `(cellEditExit)` | Cell exits edit mode | No |
|
|
347
|
+
| `(rowEdit)` | Row edit is about to be committed (Done button) | Yes |
|
|
348
|
+
| `(rowEditDone)` | Row edit has been committed | No |
|
|
349
|
+
| `(rowEditExit)` | Row exits edit mode | No |
|
|
350
|
+
|
|
351
|
+
Canceling `(cellEdit)` keeps the cell in edit mode — the value won't commit until Cancel is clicked:
|
|
352
|
+
|
|
353
|
+
```typescript
|
|
354
|
+
onCellEdit(event: IGridEditEventArgs) {
|
|
355
|
+
if (!event.valid) {
|
|
356
|
+
event.cancel = true; // prevent committing invalid values
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## Validation
|
|
362
|
+
|
|
363
|
+
> **Docs:** [Validation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/validation)
|
|
364
|
+
|
|
365
|
+
### Template-Driven Validation
|
|
366
|
+
|
|
367
|
+
Apply Angular validators directly on columns:
|
|
368
|
+
|
|
369
|
+
```html
|
|
370
|
+
<igx-column field="email" [editable]="true" required email></igx-column>
|
|
371
|
+
<igx-column field="age" dataType="number" [editable]="true" required [min]="18" [max]="120"></igx-column>
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Supported built-in validators: `required`, `min`, `max`, `email`, `minlength`, `maxlength`, `pattern`.
|
|
375
|
+
|
|
376
|
+
### Reactive Form Validation
|
|
377
|
+
|
|
378
|
+
Use the `formGroupCreated` event to add custom validators when a row enters edit mode:
|
|
379
|
+
|
|
380
|
+
```html
|
|
381
|
+
<igx-grid #grid [data]="data()" [rowEditable]="true" [primaryKey]="'id'"
|
|
382
|
+
(formGroupCreated)="onFormGroupCreated($event)">
|
|
383
|
+
</igx-grid>
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
```typescript
|
|
387
|
+
onFormGroupCreated(event: IGridFormGroupCreatedEventArgs) {
|
|
388
|
+
const { formGroup } = event;
|
|
389
|
+
formGroup.get('endDate')?.addValidators(this.dateAfterValidator('startDate'));
|
|
390
|
+
}
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### Validation Service
|
|
394
|
+
|
|
395
|
+
The grid exposes a `validation` service:
|
|
396
|
+
|
|
397
|
+
```typescript
|
|
398
|
+
// Check if the grid is in a valid state
|
|
399
|
+
const isValid = this.gridRef().validation.valid;
|
|
400
|
+
|
|
401
|
+
// Get all records with validation errors
|
|
402
|
+
const invalid = this.gridRef().validation.getInvalid();
|
|
403
|
+
|
|
404
|
+
// Clear validation state for a specific record (or all if no id)
|
|
405
|
+
this.gridRef().validation.clear(recordId);
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
## Summaries
|
|
409
|
+
|
|
410
|
+
> **Docs:** [Summaries](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/summaries) (substitute URL prefix per grid type)
|
|
411
|
+
|
|
412
|
+
### Built-In Summaries
|
|
413
|
+
|
|
414
|
+
```html
|
|
415
|
+
<igx-column field="amount" dataType="number" [hasSummary]="true"></igx-column>
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
Default summaries by type:
|
|
419
|
+
- **number**: Count, Min, Max, Sum, Average
|
|
420
|
+
- **date**: Count, Earliest, Latest
|
|
421
|
+
- **string/boolean**: Count
|
|
422
|
+
|
|
423
|
+
### Custom Summary Operand
|
|
424
|
+
|
|
425
|
+
```typescript
|
|
426
|
+
import { IgxNumberSummaryOperand, IgxSummaryResult } from 'igniteui-angular/grids/core';
|
|
427
|
+
|
|
428
|
+
class RevenueSummary extends IgxNumberSummaryOperand {
|
|
429
|
+
operate(data: number[]): IgxSummaryResult[] {
|
|
430
|
+
const result = super.operate(data);
|
|
431
|
+
result.push({
|
|
432
|
+
key: 'margin',
|
|
433
|
+
label: 'Avg Margin',
|
|
434
|
+
summaryResult: data.length ? data.reduce((a, b) => a + b, 0) / data.length * 0.15 : 0
|
|
435
|
+
});
|
|
436
|
+
return result;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Use in component
|
|
441
|
+
revenueSummary = RevenueSummary;
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
```html
|
|
445
|
+
<igx-column field="revenue" dataType="number" [hasSummary]="true" [summaries]="revenueSummary"></igx-column>
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
### Summaries with Grouping
|
|
449
|
+
|
|
450
|
+
When grouping is enabled, summaries appear for each group. Control this with:
|
|
451
|
+
|
|
452
|
+
```html
|
|
453
|
+
<igx-grid #grid
|
|
454
|
+
[data]="data()"
|
|
455
|
+
[showSummaryOnCollapse]="true"
|
|
456
|
+
[summaryCalculationMode]="'childLevelsOnly'"
|
|
457
|
+
[summaryPosition]="'bottom'">
|
|
458
|
+
</igx-grid>
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
## Key Rules
|
|
462
|
+
|
|
463
|
+
1. **Choose the right editing mode** — cell editing (`[editable]` + `(cellEditDone)`) for immediate per-cell saves; row editing (`[rowEditable]="true"` + `(rowEditDone)`) for confirm/cancel per row (**recommended default for CRUD**); batch editing (`[batchEditing]="true"`) for accumulate-then-commit with undo/redo
|
|
464
|
+
2. **`[primaryKey]` is required for all editing** — row editing, batch editing, row adding, and row deletion all depend on it (Flat, Tree, Hierarchical, Pivot grids; NOT Grid Lite)
|
|
465
|
+
3. **Always set `[autoGenerate]="false"` when editing** — define columns explicitly and mark each with `[editable]="true"` to control exactly what users can change
|
|
466
|
+
4. **Batch editing requires `[primaryKey]`** — call `endEdit(true)` before `transactions.undo()`/`redo()`, commit via `transactions.commit(data)`
|
|
467
|
+
5. **Cancelable events** — use `event.cancel = true` in `(cellEdit)`, `(rowEdit)`, `(paging)` to prevent the action
|
|
468
|
+
6. **Validation** — use template-driven validators on columns (`required`, `min`, `max`, `email`, `pattern`) or reactive validators via `(formGroupCreated)`
|
|
469
|
+
7. **Use the correct component type for `viewChild`** — `IgxGridComponent`, `IgxTreeGridComponent`, `IgxHierarchicalGridComponent`, or `IgxPivotGridComponent`
|
|
470
|
+
8. **Import the correct directives/components** — `IGX_GRID_DIRECTIVES`, `IGX_TREE_GRID_DIRECTIVES`, `IGX_HIERARCHICAL_GRID_DIRECTIVES`, or `IGX_PIVOT_GRID_DIRECTIVES`
|
|
471
|
+
9. **Use signals for data** — `[data]="myData()"` with `signal<T[]>([])`
|
|
472
|
+
|
|
473
|
+
## See Also
|
|
474
|
+
|
|
475
|
+
- [`data-operations.md`](./data-operations.md) — Sorting, filtering, grouping, and canonical grid import patterns
|
|
476
|
+
- [`paging-remote.md`](./paging-remote.md) — Paging, remote data operations, virtualization
|
|
477
|
+
- [`state.md`](./state.md) — State persistence, Tree Grid / Hierarchical Grid / Pivot Grid / Grid Lite data operations
|
|
478
|
+
- [`structure.md`](./structure.md) — Grid structure, column configuration, templates, layout, selection
|
|
479
|
+
- [`../../igniteui-angular-components/SKILL.md`](../../igniteui-angular-components/SKILL.md) — Non-grid Ignite UI components
|
|
480
|
+
- [`../../igniteui-angular-theming/SKILL.md`](../../igniteui-angular-theming/SKILL.md) — Theming & Styling
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Grid Features — Grouping, Summaries, Toolbar, Export, Row Drag & More
|
|
2
|
+
|
|
3
|
+
> **Part of the [`igniteui-angular-grids`](../SKILL.md) skill hub.**
|
|
4
|
+
> For grid setup, column config, sorting, filtering, selection — see [`structure.md`](./structure.md).
|
|
5
|
+
> For Tree Grid, Hierarchical Grid, Grid Lite, Pivot Grid specifics — see [`types.md`](./types.md).
|
|
6
|
+
> For full editing coverage (cell/row/batch) — see [`editing.md`](./editing.md).
|
|
7
|
+
|
|
8
|
+
## Editing
|
|
9
|
+
|
|
10
|
+
> **Full editing coverage is in [`editing.md`](./editing.md)**, which includes cell editing, row editing, batch editing with transactions, row adding/deleting, validation, and summaries. Use that reference for any editing task.
|
|
11
|
+
|
|
12
|
+
Quick reference:
|
|
13
|
+
|
|
14
|
+
| Mode | Key properties |
|
|
15
|
+
|---|---|
|
|
16
|
+
| **Cell editing** | `[editable]="true"` on columns + `(cellEditDone)` |
|
|
17
|
+
| **Row editing** (recommended default) | `[rowEditable]="true"` + `[editable]="true"` on columns + `(rowEditDone)` |
|
|
18
|
+
| **Batch editing** | `[batchEditing]="true"` + `[rowEditable]="true"` + `transactions.commit(data)` |
|
|
19
|
+
|
|
20
|
+
## Grouping (Grid only)
|
|
21
|
+
|
|
22
|
+
> **Docs:** [Group By](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/groupby)
|
|
23
|
+
|
|
24
|
+
```html
|
|
25
|
+
<igx-grid [data]="data()" [groupsExpanded]="true">
|
|
26
|
+
<igx-column field="category" [groupable]="true"></igx-column>
|
|
27
|
+
|
|
28
|
+
<!-- Custom group row template -->
|
|
29
|
+
<ng-template igxGroupByRow let-groupRow>
|
|
30
|
+
{{ groupRow.expression.fieldName }}: {{ groupRow.value }} ({{ groupRow.records.length }} items)
|
|
31
|
+
</ng-template>
|
|
32
|
+
</igx-grid>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Programmatic:
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
this.gridRef().groupBy({ fieldName: 'category', dir: SortingDirection.Asc });
|
|
39
|
+
this.gridRef().clearGrouping('category');
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
For advanced programmatic grouping patterns — see [`data-operations.md`](./data-operations.md).
|
|
43
|
+
|
|
44
|
+
## Summaries
|
|
45
|
+
|
|
46
|
+
> **Full summaries coverage (built-in and custom summary operands) is in [`editing.md`](./editing.md).**
|
|
47
|
+
|
|
48
|
+
Quick reference — enable per-column summaries:
|
|
49
|
+
|
|
50
|
+
```html
|
|
51
|
+
<igx-column field="salary" dataType="number" [hasSummary]="true"></igx-column>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Default summaries by type: **number** → Count/Min/Max/Sum/Average; **date** → Count/Earliest/Latest; **string/boolean** → Count.
|
|
55
|
+
|
|
56
|
+
## Cell Merging
|
|
57
|
+
|
|
58
|
+
Merge adjacent cells with equal values:
|
|
59
|
+
|
|
60
|
+
```html
|
|
61
|
+
<igx-column field="category" [merge]="true"></igx-column>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Or apply a custom merge strategy:
|
|
65
|
+
|
|
66
|
+
```html
|
|
67
|
+
<igx-column field="price" [merge]="true" [mergeStrategy]="priceRangeMerge"></igx-column>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
priceRangeMerge: IgxCellMergeStrategy = {
|
|
72
|
+
shouldMerge(prevCell, curCell) {
|
|
73
|
+
return Math.abs(prevCell.value - curCell.value) < 10;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Toolbar
|
|
79
|
+
|
|
80
|
+
> **Docs:** [Toolbar](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/toolbar)
|
|
81
|
+
|
|
82
|
+
```html
|
|
83
|
+
<igx-grid [data]="data()">
|
|
84
|
+
<igx-grid-toolbar>
|
|
85
|
+
<igx-grid-toolbar-title>Products</igx-grid-toolbar-title>
|
|
86
|
+
<igx-grid-toolbar-actions>
|
|
87
|
+
<igx-grid-toolbar-hiding></igx-grid-toolbar-hiding>
|
|
88
|
+
<igx-grid-toolbar-pinning></igx-grid-toolbar-pinning>
|
|
89
|
+
<igx-grid-toolbar-exporter></igx-grid-toolbar-exporter>
|
|
90
|
+
<igx-grid-toolbar-advanced-filtering></igx-grid-toolbar-advanced-filtering>
|
|
91
|
+
</igx-grid-toolbar-actions>
|
|
92
|
+
</igx-grid-toolbar>
|
|
93
|
+
|
|
94
|
+
<igx-column field="name"></igx-column>
|
|
95
|
+
</igx-grid>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Export
|
|
99
|
+
|
|
100
|
+
### Excel Export
|
|
101
|
+
|
|
102
|
+
> **Docs:** [Excel Export](https://www.infragistics.com/products/ignite-ui-angular/angular/components/exporter-excel)
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { IgxExcelExporterService, IgxExcelExporterOptions } from 'igniteui-angular/grids/core';
|
|
106
|
+
|
|
107
|
+
export class MyComponent {
|
|
108
|
+
private excelExporter = inject(IgxExcelExporterService);
|
|
109
|
+
|
|
110
|
+
exportToExcel() {
|
|
111
|
+
this.excelExporter.exportData(this.data(), new IgxExcelExporterOptions('export'));
|
|
112
|
+
// Or export the grid (respects filtering/sorting)
|
|
113
|
+
this.excelExporter.export(this.grid, new IgxExcelExporterOptions('export'));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### CSV Export
|
|
119
|
+
|
|
120
|
+
> **Docs:** [CSV Export](https://www.infragistics.com/products/ignite-ui-angular/angular/components/exporter-csv)
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
import { IgxCsvExporterService, IgxCsvExporterOptions, CsvFileTypes } from 'igniteui-angular/grids/core';
|
|
124
|
+
|
|
125
|
+
export class MyComponent {
|
|
126
|
+
private csvExporter = inject(IgxCsvExporterService);
|
|
127
|
+
|
|
128
|
+
exportToCsv() {
|
|
129
|
+
this.csvExporter.export(this.grid, new IgxCsvExporterOptions('export', CsvFileTypes.CSV));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Virtualization & Performance
|
|
135
|
+
|
|
136
|
+
Grids use virtualization by default for both rows and columns — no setup needed. For remote data/paging:
|
|
137
|
+
|
|
138
|
+
```html
|
|
139
|
+
<igx-grid [data]="data()" [totalItemCount]="totalCount" (dataPreLoad)="onDataPreLoad($event)">
|
|
140
|
+
</igx-grid>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
For full remote virtualization patterns — see [`paging-remote.md`](./paging-remote.md).
|
|
144
|
+
|
|
145
|
+
## Row Drag
|
|
146
|
+
|
|
147
|
+
> **Docs:** [Row Drag](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/row-drag)
|
|
148
|
+
|
|
149
|
+
```html
|
|
150
|
+
<igx-grid [rowDraggable]="true" (rowDragStart)="onDragStart($event)" (rowDragEnd)="onDragEnd($event)">
|
|
151
|
+
<ng-template igxRowDragGhost let-dragData>
|
|
152
|
+
<span>Moving {{ dragData.dragData.name }}</span>
|
|
153
|
+
</ng-template>
|
|
154
|
+
</igx-grid>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Action Strip
|
|
158
|
+
|
|
159
|
+
> **Docs:** [Action Strip](https://www.infragistics.com/products/ignite-ui-angular/angular/components/action-strip)
|
|
160
|
+
|
|
161
|
+
Overlay actions on a row:
|
|
162
|
+
|
|
163
|
+
```html
|
|
164
|
+
<igx-grid [data]="data()">
|
|
165
|
+
<igx-action-strip>
|
|
166
|
+
<igx-grid-editing-actions [addRow]="true"></igx-grid-editing-actions>
|
|
167
|
+
<igx-grid-pinning-actions></igx-grid-pinning-actions>
|
|
168
|
+
</igx-action-strip>
|
|
169
|
+
<igx-column field="name"></igx-column>
|
|
170
|
+
</igx-grid>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Master-Detail (Grid only)
|
|
174
|
+
|
|
175
|
+
> **Docs:** [Master-Detail](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/master-detail)
|
|
176
|
+
|
|
177
|
+
Expand rows to show arbitrary detail content:
|
|
178
|
+
|
|
179
|
+
```html
|
|
180
|
+
<igx-grid [data]="orders()" [primaryKey]="'orderId'">
|
|
181
|
+
<igx-column field="orderId"></igx-column>
|
|
182
|
+
<igx-column field="customer"></igx-column>
|
|
183
|
+
|
|
184
|
+
<ng-template igxGridDetail let-dataItem>
|
|
185
|
+
<div class="detail-container">
|
|
186
|
+
<h4>Order Items for {{ dataItem.customer }}</h4>
|
|
187
|
+
<igx-grid [data]="dataItem.items" [autoGenerate]="true" height="200px">
|
|
188
|
+
</igx-grid>
|
|
189
|
+
</div>
|
|
190
|
+
</ng-template>
|
|
191
|
+
</igx-grid>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Clipboard
|
|
195
|
+
|
|
196
|
+
Grids support copy to clipboard by default. Configure via:
|
|
197
|
+
|
|
198
|
+
```html
|
|
199
|
+
<igx-grid [clipboardOptions]="{ enabled: true, copyHeaders: true, copyFormatters: true, separator: '\t' }">
|
|
200
|
+
</igx-grid>
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Key Rules
|
|
204
|
+
|
|
205
|
+
1. **Cancelable events** — use `event.cancel = true` in `(rowEdit)`, `(cellEdit)`, `(sorting)`, `(filtering)` to prevent the action
|
|
206
|
+
2. **Use signals** for data binding — `[data]="myData()"` with `signal<T[]>([])`
|
|
207
|
+
3. **Virtualization is automatic** — don't wrap grids in virtual scroll containers
|
|
208
|
+
4. **GroupBy is Flat Grid only** — Tree Grid uses hierarchy, Hierarchical Grid uses row islands, Pivot Grid uses dimensions
|
|
209
|
+
|
|
210
|
+
## See Also
|
|
211
|
+
|
|
212
|
+
- [`structure.md`](./structure.md) — Column config, sorting UI, filtering UI, selection
|
|
213
|
+
- [`types.md`](./types.md) — Tree Grid, Hierarchical Grid, Grid Lite, Pivot Grid specifics
|
|
214
|
+
- [`data-operations.md`](./data-operations.md) — Programmatic sorting, filtering, grouping
|
|
215
|
+
- [`paging-remote.md`](./paging-remote.md) — Paging, remote data operations, virtualization
|
|
216
|
+
- [`editing.md`](./editing.md) — Cell editing, row editing, batch editing, validation, summaries
|
|
217
|
+
- [`state.md`](./state.md) — State persistence
|
|
218
|
+
- [`../../igniteui-angular-theming/SKILL.md`](../../igniteui-angular-theming/SKILL.md) — Grid styling and theming
|