Grid API
IGridAPI is available from React's ref/onGridReady, Angular's gridReady
output or template reference, and createGrid() in a core integration.
Reconfiguring a mounted grid
| Method | Purpose |
|---|---|
updateGridOptions(options) | Change presentation and behavior options in place, preserving scroll, selection, focus, and edit history |
registerMenuAdapter(adapter) | Install (or remove, with null) the column-menu adapter on a mounted grid |
registerBodyMenuAdapter(adapter) | The same for the body context menu |
Only the properties present in options change; a property present with the
value undefined resets to the grid's default, which is how a callback such as
getRowStyle is removed. Object values replace wholesale — they are not
deep-merged.
api.updateGridOptions({ toolbar: { sorting: true, export: true } });
api.updateGridOptions({ zebraRows: true, rowHover: false });
api.updateGridOptions({ tooltip: { mode: "follow" } });
api.updateGridOptions({ getRowStyle: undefined }); // back to the default
This is what the React and Angular bindings do when a prop changes, so a
framework-neutral host reaches the same behavior without recreating the grid.
UpdatableGridOptions lists what can change: visual and interaction options,
toolbar / quick filter / tooltip / column panel / saved views, pagination and its
controls, row selection, theme and icons, pinned-row bands, row-group
presentation, the server-side data source, and columnDefs.
Options that seed structure are fixed at creation and are rejected with a
warning: rowHeight and the header heights (virtualization geometry),
rowNumbers and rowModelType (which columns and row model exist), and
getRowId / rowIdKey (row identity). Changing one of those means creating a
new grid.
The menu adapters are not options — they are assembly ingredients, like
columnDefs, so they have their own registration methods rather than a key in
updateGridOptions:
api.registerMenuAdapter(adapter); // effective on the next menu open
api.registerBodyMenuAdapter(null); // remove; back to the built-in body menu
Both are the deferred form of createGrid's menuAdapter / bodyMenuAdapter
options, for a host that does not know its adapter when the grid is created.
Registration needs no rebuild, and a menu that is already open keeps the items
and cleanup it was given. Reach for an adapter only when menu items are
rendered by a framework and must be unmounted again — application-owned items
belong in ColDef.columnMenu, multiColumnMenu, or bodyContextMenu, all of
which run before the adapter.
Data and columns
| Method | Purpose |
|---|---|
setRowData(rows) | Replace client-side rows |
forEachNodeAfterFilter(callback) | Visit rows that pass filtering, before client-side sorting |
forEachNodeAfterFilterAndSort(callback) | Visit rows that pass filtering in final sorted order |
applyTransaction(tx) | Add, optionally insert at source-order addIndex, update, or remove client-side rows; returns { added, updated, removed } counts of what was actually applied (unknown row IDs are skipped and not counted) |
applyTransactionAsync(tx) | Mutate client-side rows immediately and defer model derivation/rendering into a shared batch; resolves after the flush with this call's counts |
flushAsyncTransactions() | Immediately finalize pending asynchronous transactions |
setColumnDefs(defs) | Replace column definitions (claims caller ownership of the schema — see below) |
getColumnState() | Capture width, order, pinning, and visibility |
applyColumnState(state, options?) | Merge or exactly restore state |
getColumnModel() | Read runtime columns and sections |
refreshServerSideData(options?) | Soft-refresh or purge a server cache/subtree |
refreshRowPresentation() | Re-evaluate row presentation for rendered body, pinned, and sticky rows after external state changes |
Who owns the column schema. A server-side response can carry its own columns, and the grid
arbitrates between that and the application: once the application supplies definitions through
any door — columnDefs at creation, a wrapper prop, updateGridOptions({ columnDefs }), or
setColumnDefs — server-sent schemas are ignored. For a non-empty array, setColumnDefs(defs)
and updateGridOptions({ columnDefs: defs }) are equivalent; they differ only at the edges:
setColumnDefs([]) clears the columns and keeps ownership, while updateGridOptions({ columnDefs: [] }) clears only if the application already owned the schema (a transient empty
array from a framework render must not discard a server schema). To hand the schema back to the
server, release ownership with updateGridOptions({ columnDefs: undefined }).
View and grouping
| Method | Purpose |
|---|---|
setQuickFilter(text, options?) | Apply client-side global search |
getFilterModel() | Read the per-column filters as serializable { colId, filters, join } items |
setFilterModel(filters) | Replace all column filters (empty array clears; resets to page 1 and clears selection) |
addFilterModel(filter) | Add or replace one column's filter, keeping the others |
removeFilterModel(colId) | Remove one column's filter (no-op when it has none) |
getSetFilterValues(colId) | Set-filter columns: the value universe (null = blanks) |
getSetFilterState(colId) | Intent-level { mode, checked, unchecked }, or null when unfiltered |
checkSetFilterValue(colId, value) / uncheckSetFilterValue(colId, value) | Toggle one value by intent |
setSetFilterValues(colId, values, { mode }?) | Replace the set filter; mode pins include/exclude semantics |
setAllGroupsExpanded(expanded) | Expand/collapse all groups in one pass |
getGroupNodes() | Every group / tree node in the model (empty when not grouped); forEachNodeAfterFilter* walk data rows only |
setRowGroupColumns(colIds) / getRowGroupColumns() | Replace/read the row-grouping columns by colId (level order; empty array clears) |
setAggregates(aggregates) / getAggregates() | Replace/read the aggregate assignments as { colId, type } entries (footer totals, group totals, pivot measures; entry order sets generated pivot column order) |
setPivotMode(on) / getPivotMode() | Toggle pivot mode (client-side; off restores the pre-pivot state exactly, on reinstates the last pivot) |
setPivotColumns(colIds) / getPivotColumns() | Replace/read the pivot columns (level order; stored even while mode is off) |
getPivotResultColumns() | Descriptors of the generated pivot value columns, in header order |
setPivotColumnOrder(order) / getPivotColumnOrder() | Replace/read the manual arrangement of the generated pivot columns (leaf order by generated colId; null = canonical). Role edits reset it |
captureViewState() | Capture columns, groups, sort, filters, search, expansion, page, and the inactive pivot state layer |
applyViewState(state, options?) | Restore a captured view |
setKeyboardNavigationMode(mode) | Switch tree grid/hierarchy navigation imperatively (source: "api") |
setTreeDataKeyboardNavigationOptions(options) | Reconfigure the mode and/or the Ctrl/Cmd+Shift+Space switch (source: "options") |
Filter, grouping, aggregate, and pivot methods address columns by the public
ColDef.colId. Explicit sort models use typed dispatch() actions.
Selection and navigation
setFocusedCell, selectRange, extendRangeTo, selectRow, selectColumn,
selectAll, selectAllRows, clearSelection, and navigate mirror rendered
keyboard and pointer behavior. selectRowsById(ids, mode?) drives row
selection programmatically by stable row id ("set" | "add" | "remove").
getSelection, getSelectedRows, and getSelectedNodes return current state
(always copies — safe to mutate). selectAllRows/areAllRowsSelected span the
whole filtered set by default (selectAllScope option).
Keyboard shortcuts
| Method | Purpose |
|---|---|
registerShortcut(shortcut) | Register an application keyboard shortcut on this grid instance; returns the disposer |
getKeyboardShortcuts() | The whole binding table — built-ins and application shortcuts — as data for a shortcut reference |
const off = api.registerShortcut({
id: "approve",
chord: "mod+shift+y", // mod = Ctrl on Windows/Linux, Cmd on macOS
label: "Approve the selected rows",
run: () => approveRows(api.getSelection()),
});
Application shortcuts resolve after every built-in (override: true registers
ahead of the non-blocking built-in scopes instead), and reserved chords are
refused with an error naming the feature that owns them: Tab and Escape always;
arrows, Home/End, Enter, Space, and PageUp/PageDown only while the surface that
claims them is on (cellSelection, headerKeyboardNavigation) — a display-only
grid frees them all. mod+alt+<printable> is refused outright (Windows AltGr
reports as Ctrl+Alt). Format a chord for the user with the exported
formatChord(chord); menu items show accelerators via MenuItem.shortcut (a
display hint) or automatically when a built-in binding shares the item's
command.
Scrolling to a row, column, or cell
| Method | Purpose |
|---|---|
ensureRowVisible(rowId, { position }?) | Scroll a row into view by stable row id |
ensureColumnVisible(colId) | Scroll a column into view horizontally |
ensureCellVisible(cell, { position }?) | Both axes at once |
These are for "jump to row" flows — a validation error, a find-in-grid result, a
row a notification just arrived about — and they do whatever it takes to get the
row on screen first: collapsed group/tree ancestors are expanded (batched into
one repaint), and under pagination the grid pages to the row, firing
paginationChanged once. Rows are addressed by stable row id and columns by
colId, so you never compute a page or a view index yourself.
position says where the row should land: "auto" (default) scrolls the
minimum needed and leaves an already-visible row alone; "top", "middle", and
"bottom" place it deliberately even when it is already on screen — a jump the
user asked for is easier to follow when the row moves somewhere predictable.
"top" means the top of the usable viewport, below any sticky group headers
docked there.
Each method returns whether the target is visible afterwards. false means no
amount of scrolling would have shown it:
- an unknown row id or
colId; - a row the active filter excludes, or a column that is hidden (including one hidden by a collapsed column group — these do not expand column groups);
- on the server-side row model, a row that is not loaded. The server owns the row order, so the grid cannot work out which page an unloaded row is on; page or filter to it instead, then scroll.
A row mirrored into a frozen top/bottom band is already on screen, so revealing
it succeeds without moving the body (the band itself scrolls if it is taller
than the space it has). Both halves of ensureCellVisible are attempted
independently — a bad colId still gets you to the row — but it returns true
only when both succeeded.
if (!api.ensureCellVisible({ rowId: error.rowId, colId: error.field }, { position: "middle" })) {
// The row is filtered out or unloaded — clear the filter (or page to it) and try again.
}
Editing and clipboard
| Method | Purpose |
|---|---|
startEditingCell(cell) | Begin editing { rowId, colId } |
stopEditing(value) / cancelEditing() | Finish or cancel the active edit |
setCellValue(cell, value) | Write one cell without opening an editor |
setCellValues(edits) | Write many cells as one undo step (changed cells only) |
copySelection() / cutSelection() / paste() | Clipboard workflows |
undo() / redo() | Traverse edit history |
canUndo() / canRedo() / clearHistory() | Inspect or reset history |
getHistoryState() | { canUndo, canRedo, undoDepth, redoDepth } |
withUndoGroup(fn) | Coalesce the writes in fn into one undo step |
withoutUndoHistory(fn) | Apply the writes in fn without recording them |
A programmatic write states its own type: a string value runs through the
column's valueParser (like typed input), anything else is stored as the final
value. So setCellValue(cell, 99) stores the number 99 even with no parser
configured, while setCellValue(cell, "99") gives the parser its say.
Floating UI and pinned rows
showTooltip, hideTooltip, openActionFrame, and closeActionFrame address
cells by stable row and column IDs. setPinnedTopRowData,
setPinnedBottomRowData, and setRowPinned update frozen row bands.
Rows supplied through pinnedTopRowData or pinnedBottomRowData stay fixed in
their band: filtering, sorting, and pagination apply only to body rows. They
are also outside row selection, so selectAllRows(), selectRowsById(), and
the selected-row APIs do not include them. Pinned cells can still be focused,
edited, and included in cell-range or column selections.
Export
exportDataAsCsv and exportDataAsExcel download files. getDataAsCsv and
getDataAsExcel return content for custom upload, encryption, or storage.
Events and cleanup
Subscribe with api.on(eventName, handler) and call the returned unsubscribe
function. destroy() releases the grid when a custom host owns its lifecycle.
filterChanged is the canonical "effective filter changed" signal: it fires
once for every column-filter model change (API, filter menu, dispatch), every
quick-filter change (source: "quickFilter", client-side row model only), and
any columnDefs update that drops an active filter (source: "columns") — no
need to subscribe to both columnsChanged {reason:"filter"} and
modelUpdated {reason:"filter"} (both still fire for back-compat). On the
client-side row model the handler runs after the view is re-derived, so row
counts and getPaginationInfo() read post-filter state. The declarative
onFilterChanged option callback wraps it.
historyChanged fires whenever the undo/redo stacks move, with
{ reason, canUndo, canRedo, undoDepth, redoDepth } — enough to drive toolbar
button state without polling. It follows the write's own cellValueChanged /
cellsChanged events, so a handler reading getHistoryState() sees the value
and the history agree. Writes that never enter history — a vetoed commit, a
commit that doesn't change the stored value, anything under readOnlyEdit,
applyTransaction, or a write inside withoutUndoHistory — do not fire it,
and a whole withUndoGroup scope fires once on exit. The declarative option
callback is onHistoryChanged.
Column identity in payloads and inputs
Every colId field carried by event payloads (cellsChanged, cellValueChanged,
editingChanged, columnsChanged, columnWidthsChanged, cellClicked,
filterChanged, tooltip
and focus events, onCellValueChanged, onSortChanged, selection rangeCells, …)
holds the public ColDef.colId — the id you defined. The column's internal
per-instance UUID rides alongside in a colInstanceId / colInstanceIds /
changedColInstanceIds field; you only need it to disambiguate the rare case of
split/moved column duplicates that share a colId, or to key caches that must
survive such splits.
Every column id the grid accepts (API methods, dispatched actions, CellRef
inputs) is resolved tolerantly — instance id, public colId, or key all work —
so api.startEditingCell({ rowId: "1", colId: "price" }) just works.