Client-side row model
The client-side model keeps row data in the browser and applies filtering, sorting, pagination, grouping, aggregation, and tree relationships locally. It is the default model.
- React
- Angular
- Core TypeScript
<Grid
rowIdKey="id"
rowData={rows}
columnDefs={columns}
pagination
pageSize={25}
pageSizes={[10, 25, 50]}
onGridReady={(api) => {
api.applyTransaction({ add: [newOrder], addIndex: 2 });
}}
/>
<awb-grid
rowIdKey="id"
[rowData]="rows"
[columnDefs]="columns"
[pagination]="true"
[pageSize]="25"
[pageSizes]="[10, 25, 50]"
(gridReady)="api = $event"
/>
// Later
this.api.applyTransaction({ add: [newOrder], addIndex: 2 });
const core = new GridCore(measurer, {
rowIdKey: "id",
columnDefs,
pagination: true,
pageSize: 25,
});
api.setRowData(rows);
api.applyTransaction({
add: [newOrder],
addIndex: 2,
update: [{ rowId: "order-1", row: changedOrder }],
remove: ["order-2"],
});
Stable row identity
Use rowIdKey when the ID is a field, or getRowId(row) for a derived value.
Stable IDs preserve row-node identity across transactions and let editing,
selection, grouping, and pinned rows refer to the same record.
const options = {
rowIdKey: "id",
rowModelType: "clientSide", // the default
} satisfies GridOptions;
// Derived or nested IDs:
const derived = {
getRowId: (row) => String(row.account.id),
} satisfies GridOptions;
Replace or transact
api.setRowData(rows) supplies a whole data set; whether that resets the grid
or is diffed into it depends on rowDataMode (see below).
api.applyTransaction({ add, addIndex, update, remove }) changes only affected rows and
returns { added, updated, removed } counts of what was actually applied
(unknown row IDs are skipped). Updates preserve node identity; structural
changes are sorted and filtered into their correct location.
Use addIndex to insert new rows at a zero-based position in the underlying
client-side row order:
api.applyTransaction({
add: [{ id: "c", name: "Gamma" }],
addIndex: 2,
update: [{ rowId: "a", row: { id: "a", name: "Alpha updated" } }],
remove: ["b"],
});
The index is evaluated after removals in the same transaction. It defaults to the end, and out-of-range values are clamped. It is a source-order position: active sorting, filtering, grouping, tree relationships, or pagination may put the inserted row at a different displayed position.
Transactions are client-side only. The server-side model is refreshed through
api.refreshServerSideData().
Batch high-frequency updates
For isolated changes, applyTransaction() updates the model and rendered view
synchronously. For a stream of small updates, use applyTransactionAsync() so
nearby calls share one filter, sort, grouping, aggregation, pagination, and
render pass:
const result = await api.applyTransactionAsync({
update: [{ rowId: quote.symbol, row: quote }],
});
Row nodes are mutated immediately and transactions retain call order. The returned promise resolves after the batch is reflected in the derived view and renderer. Until then, direct row-node reads can see new data while sorted, filtered, or grouped positions still describe the previous render.
The first call opens a fixed asyncTransactionWaitMs window (16 ms by default);
later calls join it without postponing its deadline. Use
api.flushAsyncTransactions() when a caller needs to finalize the view early.
Each promise receives the counts for its own transaction. A later synchronous
transaction or row-model operation flushes older queued work first; a new
rowData replacement subsumes the pending render in its own full refresh.
How a new rowData reference is applied
The React and Angular bindings compare rowData by reference, so the immutable
update patterns those frameworks encourage ([...rows], reducers, signals and
state setters) hand the grid a replacement array on every change. What the grid
does with it depends on rowDataMode:
rowDataMode | What happens |
|---|---|
"diff" | The new array is diffed against the current rows by ID and applied as a transaction. Row nodes keep their identity, and undo/redo history and the page index survive. |
"reset" | The whole data set is re-ingested: undo/redo history is discarded and the grid returns to page 1. |
"auto" (default) | "diff" when it is available, "reset" otherwise. |
Sort, filter, quick-filter, column state, row selection and group expansion survive in either mode.
Diffing needs a stable identity for each row, so it is used only when the
client-side row model is in play and rowIdKey or getRowId is set — without
one the grid cannot tell an edited row from a new one. Tree data always resets,
because its hierarchy is derived from the array rather than from row membership
alone. Setting rowDataMode="diff" explicitly when those conditions are not met
logs a warning and falls back.
A row counts as changed only when its object reference differs from the one
the grid already holds. That matches immutable updates, where a changed row is a
new object. If your application mutates row objects in place and then passes
a new array wrapper around them, the diff sees nothing to update and the display
goes stale — use rowDataMode="reset" in that case.
api.setRowData(rows) follows the same rules; api.applyTransaction({ add, addIndex, update, remove }) remains the most direct way to express an incremental change
when you already know what changed.