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