Selection and navigation
Cell, range, row, and column selection share one model and work with pointer, keyboard, clipboard, export, and API workflows.
- React
- Angular
- Core TypeScript
<Grid
rowData={rows}
columnDefs={columns}
rowNumbers
rowSelection
selectAllRowsOnHeaderClick
highlightActiveCell
onSelectionChanged={({ snapshot }) => console.log(snapshot)}
/>
<awb-grid
[rowData]="rows"
[columnDefs]="columns"
[rowNumbers]="true"
[rowSelection]="true"
[selectAllRowsOnHeaderClick]="true"
[highlightActiveCell]="true"
(selectionChanged)="selection = $event.snapshot"
/>
const core = new GridCore(measurer, {
columnDefs,
rowNumbers: true,
rowSelection: true,
selectAllRowsOnHeaderClick: true,
highlightActiveCell: true,
});
api.selectRange(2, 1);
api.extendRangeTo(8, 3);
console.log(api.getSelection());
Selection modes
cellSelection: trueenables grid focus and selection.cellSelection: "text"restores native browser text selection. Both"text"andfalsealso disable the body keyboard cursor: navigation, clipboard, and editing keys operate on the cursor, so they go with it (keyboard row selection included). The header keyboard cursor stays — disable it separately withheaderKeyboardNavigation: falseif the grid is deliberately inert.rangeSelectionenables drag and Shift-extension.fillHandle(default on) puts the spreadsheet fill handle on the range's bottom-right corner; see Editing → Fill handle.rowSelectionuses the row-number cells for row selection.rowSelection: { mode: "multiple", checkboxes: true }adds a dedicated checkbox column (independent ofrowNumbers): click toggles the row — always additive, never clearing the rest — and Shift+click unions a range in. A tri-state select-all checkbox renders in its header (disable withheaderCheckbox: false), covering the select-all scope (selectAllScope). Usemode: "single"to keep at most one row selected; single mode always hides the header checkbox.- The checkbox column starts pinned left. Set
checkboxColumnPinnedto"right"ornullto change its initial position, and setcheckboxColumnPinnable: falseto lock it there. These are separate options, so any initial position can be either fixed or user-changeable. columnSelectionuses headers for column selection.
Arrow keys navigate one cell. Home/End, Page Up/Down, Ctrl/Cmd+Arrow block jumps, and Ctrl/Cmd+Home/End corner jumps keep the active cell visible. Ctrl/Cmd+A selects the full unified row sequence.
On the header cursor, Space selects the column, Ctrl/Cmd+Space adds one to
the selection, and Shift+Arrow (or Shift+Home/Shift+End) extends the
selection from the cursor's column — but only while the cursor is on a selected
column, so a Shift chord never reads as plain movement that starts selecting.
The API exposes focused-cell, range, row, column, navigation, and selection snapshot operations. Pagination invalidates a range from the previous page.
// Rows
api.selectRow(3, "replace");
api.selectAllRows();
console.log(api.getSelectedRows());
api.deselectAllRows();
// Columns (columnSelection: true)
api.selectColumn("revenue", "replace");
api.selectColumn("cost", "toggle");
// Ranges
api.selectRange(2, 1); // row index 2, column index 1
api.extendRangeTo(8, 3);
console.log(api.getSelection());
api.clearSelection("range");
// Keyboard-style navigation
api.setFocusedCell(0, 0);
api.navigate("down");
api.navigate("right", { extend: true });
api.navigate("down", { jump: "block" });
api.navigate("down", { jump: "page", pageRows: 20 });
api.navigateToCorner("bottomRight");
Row selection for an external owner
Row selection is keyed by stable row id, so an application store can drive it directly:
api.selectRowsById(["r1", "r7"]); // replace the selection
api.selectRowsById(["r9"], "add"); // extend it
api.selectRowsById(["r1"], "remove"); // shrink it
api.getSelection().selectedRowIds; // read it back
Unknown or non-selectable ids are dropped (client-side row model). Rows that
are currently filtered out can still be selected by id. Programmatic changes
emit selectionChanged with reason: "api"; pointer/keyboard interactions
report "mouse"/"keyboard", and filter/sort-driven clears report "model".
The event's delta: { added, removed } lists row-id membership changes since
the previous selection event, so an external owner can update incrementally.
Client-side transactions and row-data replacements automatically remove
selected ids for rows that no longer exist and report that delta as a model change.
Select-all scope. api.selectAllRows(), api.areAllRowsSelected(), and
the row-number header click cover the whole filtered set — all pages — by
default. Set selectAllScope: "page" for page-only select-all. On the
server-side row model, "filtered" covers every row the server would return,
loaded or not — see Server-side select-all.
Groups select their children. By default a group row is not part of row
selection: its checkbox is inert, and select-all covers the data rows only. Set
groupSelectsChildren: true to make a group's checkbox stand for its subtree.
Clicking it (or Space/Enter with the cursor on it) selects every selectable
leaf under the group, collapsed ones included, or clears them all when all are
already selected — so the first click on a partly selected group completes it.
Nested groups follow, and a tree-data parent selects itself along with its
descendants. The group's checkbox shows the derived state: ticked when all of
its selectable rows are selected, a dash (aria-checked="mixed") when some
are, empty when none are, and disabled when nothing under it is selectable. A
fully selected group row paints as selected.
- Storage stays data rows. Group rows are never stored:
getSelectedRows(),getSelectedNodes()andselectedRowIdshold leaves (and tree parents) only, even for a fully selected group. Select-all, the header tri-state andareAllRowsSelected()cover those same rows. - What counts. Filtered-out rows never join, and rows your
isRowSelectablerefuses are skipped and do not count toward the group's state. A tree parent that is on screen only as the ancestor of a matching row is not selected. - Ranges. Shift+click unions the rows between the anchor and the clicked
row, and a group row inside the range contributes its whole subtree.
selectAllScope: "page"works the same way: a collapsed group on the page brings its hidden leaves with it. - Keyboard. In the checkbox column the cursor stops on group rows, so
Space/Entercan toggle them; their other cells stay out of cell selection unlessgroupRowsSelectableis on. - Not the same as
groupRowsSelectable. That option makes group rows cell-selection targets — the cursor, ranges and the clipboard — and, on its own, lets a group row be checked as a row in itself. UndergroupSelectsChildrena group's checkbox always means its subtree; the two options combine. - Limits. Applies in
mode: "multiple"(single mode ignores it) and not in pivot mode. On the server-side row model only loaded rows in the current view are affected: a collapsed group selects nothing below it until its rows are fetched. UndergroupDisplayType: "groupRows"the full-width group row keeps its checkbox beside the spanning label while the checkbox column is pinned (the default,rowSelection.checkboxColumnPinned: "left"); an unpinned one sits under the label, so select those rows with a Shift range instead.
Persistence. By default a filter, sort, or quick-filter change clears the
row selection. Set selectionPersistence: "keep" to retain selected row ids
across model changes — selection is id-based, so it survives rows moving pages
or temporarily leaving the filtered view (the cell range is still cleared, and
a selectionChanged with reason: "model" still fires). Exports with
scope: "selection" honor the full cross-page selection.
Server-side select-all
The server-side row model cannot list rows it has not fetched, so select-all there is a state, not a list of ids: "every row of the current query, minus these exclusions".
api.selectAllRows(); // or the header checkbox
api.getRowSelectionState(); // { selectAll: true, toggledRowIds: [] }
// the user un-checks two rows
api.getRowSelectionState(); // { selectAll: true, toggledRowIds: ["r7", "r9"] }
// Hand the server a bulk operation: it resolves "all" against the same
// filter / quick filter it is answering rows requests with.
await archiveRows({ ...api.getRowSelectionState(), filterModel: api.getFilterModel() });
- Per-row rule. A row is selected when
selectAllis on and it is not intoggledRowIds, or whenselectAllis off and it is. Rows that load later — scrolling, another page, expanding a group — paint selected without a newselectionChanged. Rows yourisRowSelectablerefuses stay unchecked and are never stored intoggledRowIds. - Gestures. A row checkbox (or
Spaceon it) toggles the row into or out of the exclusions;Shift+click on a checkbox takes a range out of them. A gesture that replaces the selection (a plain row-number click),selectRowsById(ids)("set"), un-checking the header anddeselectAllRows()leave select-all for an explicit selection.selectRowsById(ids, "remove")adds exclusions and"add"removes them. - Header checkbox. Checked under select-all with nothing excluded,
indeterminate with exclusions (or any explicit selection), empty otherwise.
areAllRowsSelected()is true only with nothing excluded. - Reading ids.
getSelectedRowIds(),getSelectedRows(),getSelectedNodes()andgetSelection().selectedRowIdsreturn the loaded selected rows only;getSelection()also carriesselectAll: trueandtoggledRowIdswhile select-all is on (so does theselectionChangedsnapshot). Exports withscope: "selection"and copying write the loaded selected rows, with a one-time console warning — run the operation on the server fromgetRowSelectionState()instead. - Restoring.
api.setRowSelectionState(state)puts a saved state back. On the client-side row model (or withselectAllScope: "page", or in single mode)selectAll: trueresolves at once to the scope's rows minus the exclusions — those configurations keep explicit ids, as before. - What ends it. A filter or quick-filter change ends select-all even under
selectionPersistence: "keep"(the "all" set changed); a sort change followsselectionPersistence;refreshServerSideData()(with or withoutpurge) keeps it, and so does paging. Replacing the data source ends it. - Group rows are never covered themselves. Under
groupSelectsChildrena group's checkbox still acts on its loaded rows only: un-checking it excludes those rows, and its state is derived from them.