Row Nodes
Every row in a GridStorm grid is represented by a RowNode<TData> object. Row nodes wrap your data objects with metadata needed for rendering, selection, grouping, aggregation, and virtual scrolling.
interface RowNode<TData = any> {
id: string;
data: TData | undefined;
sourceIndex: number;
displayIndex: number;
level: number;
rowHeight: number;
rowTop: number;
parent: RowNode<TData> | null;
children: RowNode<TData>[] | null;
expanded: boolean;
group: boolean;
groupField: string | null;
groupValue: any;
leafChildrenCount: number;
aggData: Record<string, any> | null;
selected: boolean;
selectable: boolean;
detail: boolean;
rowPinned: 'top' | 'bottom' | null;
version: number;
}
Identity Fields
| Field | Type | Description |
|---|---|---|
id |
string |
Unique row identifier. Generated by GridConfig.getRowId or defaults to the array index. Used as the key in GridState.rowNodes and for selection tracking. |
data |
TData | undefined |
Reference to the original row data object. undefined for group rows, filler rows, and loading placeholder rows. |
sourceIndex |
number |
Index in the original (unfiltered, unsorted) data array. -1 for group rows and other synthetic nodes. |
displayIndex |
number |
Index in the displayed (post-filter/sort/group/pagination) row list. Corresponds to the visual position in the grid. Used by the virtual scroller. |
Nesting
| Field | Type | Description |
|---|---|---|
level |
number |
Nesting depth for tree/group data. 0 for top-level rows. Incremented by 1 for each grouping level. |
Display Geometry
| Field | Type | Description |
|---|---|---|
rowHeight |
number |
Height of this row in pixels, as resolved from GridConfig.rowHeight. |
rowTop |
number |
Vertical offset in pixels from the top of the virtual scroll container. Used by the virtual scroller to position row elements via CSS transform. |
Tree and Group Fields
| Field | Type | Description |
|---|---|---|
parent |
RowNode<TData> | null |
Parent group node. null for top-level rows. |
children |
RowNode<TData>[] | null |
Child row nodes when this is a group row. null for leaf (non-group) rows. |
expanded |
boolean |
Whether this group row is currently expanded (children visible). Always false for non-group rows. Default: false. |
group |
boolean |
true if this node represents a group row rather than a data row. Group rows are created by the grouping plugin. |
groupField |
string | null |
The column field that this group row represents. null for non-group rows. |
groupValue |
any |
The grouping value for this group row (e.g., the department name). undefined for non-group rows. |
leafChildrenCount |
number |
Total number of leaf (non-group) descendant rows under this group. 0 for non-group rows. |
Aggregation Fields
| Field | Type | Description |
|---|---|---|
aggData |
Record<string, any> | null |
Aggregated data for group rows, keyed by column ID. Populated by the aggregation plugin when ColumnDef.aggFunc is configured. null when no aggregation is active or for non-group rows. |
// For a group row with aggFunc: 'sum' on salary, aggFunc: 'avg' on age:
node.aggData // { salary: 450000, age: 32.5 }
Selection Fields
| Field | Type | Description |
|---|---|---|
selected |
boolean |
Whether this row is currently selected. |
selectable |
boolean |
Whether this row can be selected. Set to false to prevent selection of specific rows. Default: true. |
Detail Fields
| Field | Type | Description |
|---|---|---|
detail |
boolean |
true if this is a detail row in a master-detail layout. Detail rows expand below their master row and can contain nested grids or custom content. |
Pinning Fields
| Field | Type | Description |
|---|---|---|
rowPinned |
'top' | 'bottom' | null |
Indicates whether this row is pinned. 'top' from GridConfig.pinnedTopRowData, 'bottom' from GridConfig.pinnedBottomRowData, null for normal scrolling rows. |
Version Tracking
| Field | Type | Description |
|---|---|---|
version |
number |
Incremented each time the node's properties change. Used by the renderer for targeted re-renders. Comparing versions is cheaper than deep equality checks on the entire node. |
Row Types
Data Rows
Standard rows that hold your data objects:
node.group === false
node.data !== undefined
node.children === null
node.rowPinned === null
Group Rows
Created by the grouping plugin when ColumnDef.rowGroup is enabled:
node.group === true
node.data === undefined
node.children !== null // array of child RowNodes
node.groupField === 'department'
node.groupValue === 'Engineering'
node.leafChildrenCount === 15
node.expanded === true // or false
Pinned Rows
Created from pinnedTopRowData or pinnedBottomRowData. Excluded from sorting, filtering, and pagination:
node.rowPinned === 'top' // or 'bottom'
node.data !== undefined // holds the pinned row data
Detail Rows
Used in master-detail layouts for expandable sub-content:
node.detail === true
Accessing Row Nodes
By ID
const node = api.getRowNode('emp-123');
if (node) {
console.log(node.data?.name, node.selected);
}
By Display Index
const firstRow = api.getDisplayedRowAtIndex(0);
const lastRow = api.getDisplayedRowAtIndex(api.getDisplayedRowCount() - 1);
Iterating All Nodes
api.forEachNode((node, index) => {
if (node.selected) {
console.log('Selected:', node.id, node.data?.name);
}
});
From Selected Rows
const selectedNodes = api.getSelectedNodes();
const selectedData = api.getSelectedRows(); // returns TData[]
From Grid State
const state = api.getState();
const totalNodes = state.rowNodes.size; // Map<string, RowNode>
const displayedCount = state.displayedRowIds.length; // string[]
// Look up a specific node from state
const node = state.rowNodes.get('emp-123');
Row Node Events
Many grid events include the affected row node in their payload. Subscribe via api.addEventListener.
| Event | Payload Fields | Description |
|---|---|---|
row:clicked |
{ node, event } |
Fired when a row is clicked. |
row:doubleClicked |
{ node, event } |
Fired when a row is double-clicked. |
cell:clicked |
{ node, colId, value, event } |
Fired when a cell is clicked. |
cell:doubleClicked |
{ node, colId, value, event } |
Fired when a cell is double-clicked. |
cell:editingStarted |
{ node, colId, value } |
Fired when a cell enters edit mode. |
cell:editingStopped |
{ node, colId, oldValue, newValue, cancelled } |
Fired when editing completes or is cancelled. |
cell:valueChanged |
{ node, colId, oldValue, newValue } |
Fired after a cell value is committed. |
row:groupOpened |
{ node, expanded } |
Fired when a group row is expanded or collapsed. |
rowNode:updated |
{ node } |
Fired when a row node's properties are updated. |
selection:changed |
{ selectedNodes, source } |
Fired when selected rows change. |
row:moved |
{ rowId, fromIndex, toIndex } |
Fired when a row is moved to a new position. |
api.addEventListener('cell:clicked', (event) => {
console.log('Clicked row:', event.node.id, 'column:', event.colId);
});
api.addEventListener('row:groupOpened', (event) => {
console.log('Group', event.node.groupValue, 'expanded:', event.expanded);
});
api.addEventListener('cell:valueChanged', (event) => {
console.log('Row', event.node.id, 'changed:', event.oldValue, '->', event.newValue);
});
Row Node Lifecycle
Row nodes go through several stages during their lifetime in the grid:
1. Creation
Row nodes are created when data is provided to the grid via GridConfig.rowData, api.setRowData(), or api.addRows(). The grid engine calls createRowNodes() which:
- Assigns each node a unique
id(fromgetRowIdor array index) - Sets
sourceIndexto the position in the input array - Sets
rowHeightfromGridConfig.rowHeight - Initializes
versionto0
2. Processing
After creation, the row processing pipeline runs:
- Filtering -- Nodes not matching the filter model or quick filter are excluded from display.
- Sorting -- Remaining nodes are sorted according to the sort model.
- Grouping -- If grouping is active, group nodes are created and data nodes are nested under them.
- Display assignment -- Each visible node receives a
displayIndexandrowTopvalue.
3. Mutation
Row nodes use a mutable internal design for performance. Properties that change at runtime:
| Property | Changed By | Description |
|---|---|---|
selected |
Selection plugin | Updated when rows are selected or deselected. |
expanded |
Grouping plugin / API | Updated when group rows are expanded or collapsed. |
displayIndex |
Row processing pipeline | Reassigned after sort, filter, or group changes. |
rowTop |
Row processing pipeline | Recalculated based on display position and row heights. |
aggData |
Aggregation plugin | Recomputed when group membership or values change. |
version |
Any mutation | Incremented after any property change to signal the renderer. |
4. Update
To update a cell value, use the transaction API or direct mutation:
api.updateRows([
{ id: 'emp-123', data: { salary: 95000 } },
]);
const node = api.getRowNode('emp-123');
if (node?.data) {
node.data.salary = 95000;
node.version++;
api.refreshCells({ rowIds: ['emp-123'], colIds: ['salary'] });
}
:::caution
Direct mutation bypasses the command bus and event system. For tracked, auditable changes, use api.updateRows() or the editing system instead.
:::
5. Removal
Nodes are removed via api.removeRows() or replaced entirely via api.setRowData(). Removed nodes are deleted from GridState.rowNodes and from the selection set.
api.removeRows(['emp-123', 'emp-456']);
GridState Row Collections
The grid state contains two row-related collections:
| Collection | Type | Description |
|---|---|---|
rowNodes |
Map<string, RowNode<TData>> |
All row nodes keyed by ID. Includes data rows, group rows, and pinned rows. |
displayedRowIds |
string[] |
Ordered array of row IDs currently visible after filter, sort, group, and pagination. |
const state = api.getState();
// All nodes
for (const [id, node] of state.rowNodes) {
console.log(id, node.group ? 'group' : 'data');
}
// Displayed nodes in order
for (const id of state.displayedRowIds) {
const node = state.rowNodes.get(id);
console.log(node?.displayIndex, node?.data);
}
Related Types
GetRowIdParams
interface GetRowIdParams<TData> {
data: TData; // The row data object
index: number; // Zero-based index in the input data array
parentKeys?: string[]; // Parent group keys for grouped/tree data
}
RowModelType
type RowModelType = 'client' | 'server' | 'infinite' | 'viewport';
SelectionSource
type SelectionSource = 'api' | 'checkbox' | 'click' | 'keyboard' | 'selectAll';