Tree data
Tree data represents real hierarchical records rather than value buckets. Real parent rows remain data-bearing, selectable, editable, sortable, and filterable.
- React
- Angular
- Core TypeScript
<Grid
rowIdKey="id"
rowData={rows}
columnDefs={columns}
groupDefaultExpanded={2}
treeData={{
mode: "parent",
getParentId: (row) => row.parentId,
getLabel: (row) => row.name,
columnDef: { label: "Workspace", width: 260 },
}}
/>
treeData = {
mode: "parent" as const,
getParentId: (row: Item) => row.parentId,
getLabel: (row: Item) => row.name,
columnDef: { label: "Workspace", width: 260 },
};
<awb-grid
rowIdKey="id"
[rowData]="rows"
[columnDefs]="columns"
[groupDefaultExpanded]="2"
[treeData]="treeData"
/>
const core = new GridCore(measurer, {
rowIdKey: "id",
columnDefs,
groupDefaultExpanded: 2,
treeData: {
mode: "parent",
getParentId: (row) => row.parentId,
getLabel: (row) => row.name,
columnDef: { label: "Workspace", width: 260 },
},
});
Relationship modes
| Mode | Row model | Supply |
|---|---|---|
path | client-side | getPath(row) returning the complete hierarchy path |
parent | client-side | Stable IDs plus getParentId(row) |
children | client-side | Root objects plus getChildren(row) |
server | server-side | hasChildren(row); the data source answers each parent's children |
The three client-side modes support ancestor-preserving filtering, sibling
sorting, saved expansion, sticky ancestors, selection, editing, and export. A
generated hierarchy column can be configured through treeData.columnDef in
every mode.
Sorting is per level: siblings order by their own cell values under the active
sort, and each row's children sort among themselves beneath it. A parent's
position never depends on its children — ascending, a parent Z with a child
A still follows a sibling B. A row with no values of its own to compare (a
path ancestor the grid synthesized, or a real ancestor the filters dropped
that stays only to reach a matching descendant) sits where its first child
would.
Selecting a branch. A parent row is selected like any other row: alone. Set
groupSelectsChildren: true to make its checkbox select the parent together
with every selectable descendant, collapsed branches included, and clear them
all again once all are selected. The parent's checkbox then reflects the branch:
ticked when it and all its descendants are selected, a dash when only part of
the branch is. Rows the filter removed — including an ancestor kept only to
reach a match — and rows isRowSelectable refuses are left out. With
mode: "server" only the children the grid has fetched are affected. See
Selection → Groups select their children.
Bad relationships in client-side tree data degrade rather than take the grid
down, each with one console warning per load (setRowData or transaction) that
names the offending ids or paths and how many rows were affected:
- Duplicate id: the first row with the id is kept and later ones are
dropped. In
childrenmode a dropped row's own children stay, under the kept row; the same row object listed twice counts once. - Duplicate path (
pathmode): the first row with the path owns it, and every descendant path hangs under it; each later row is shown as a childless leaf beside it. - Missing parent: the row is shown as a root.
A parent cycle still throws. The server-side tree reports its own violations
through ServerSideDataError (see below).
A mode belongs to exactly one row model. Pairing a client-side mode with
rowModelType: "serverSide" — or mode: "server" with the client-side model —
logs a warning and drops the option rather than half-applying it.
// Paths
const pathOptions = {
rowIdKey: "id",
treeData: { mode: "path", getPath: (row) => row.path },
} satisfies GridOptions;
// rows: [{ id: "paris", path: ["World", "Europe", "France", "Paris"] }]
// Parent IDs
const parentOptions = {
rowIdKey: "id",
treeData: {
mode: "parent",
getParentId: (row) => row.parentId,
getLabel: (row) => row.name,
columnDef: { label: "Organization", width: 280 },
},
} satisfies GridOptions;
// Nested children
const childrenOptions = {
rowIdKey: "id",
treeData: {
mode: "children",
getChildren: (row) => row.children,
getLabel: (row) => row.name,
},
} satisfies GridOptions;
Server-side tree data
mode: "server" moves the hierarchy to the data source: each row declares
whether it has children, and the children themselves are requested the first
time a row is expanded. Depth is unbounded and siblings are ragged — a parent
and a leaf sit side by side — because nothing is derived from column values.
const options = {
rowModelType: "serverSide",
rowIdKey: "id",
serverSideDataSource,
treeData: {
mode: "server",
hasChildren: (row) => row.kind === "folder",
getLabel: (row) => row.name,
// Naming the server's own field lets the server sort and filter the hierarchy column.
columnDef: { label: "Path", key: "name", width: 340 },
},
} satisfies GridOptions;
Requests for a parent's children carry request.treeParent — see
server-side data for the wire contract
and for refreshing one row's subtree with
refreshServerSideData({ rowId }).
Rows stay ordinary data rows: selectable, editable, copyable, and sortable, with the same chevrons, indentation, expansion state, sticky ancestors, and hierarchy keyboard mode as the client-side modes. Three consequences of the server owning the data are worth planning for:
- Row IDs must be unique across the whole tree, not per parent. Expansion
state, the node map, and subtree refreshes all key on
getRowId/rowIdKey. A children block that breaks the rule in a way the grid can be certain of — a row repeating one of its own ancestors' IDs, or one response listing an ID twice — is rejected whole and reported through theerrorevent (coderow_model_error) with aServerSideDataErroras itsdetails:reason,rowId,parentId,path, and the offendingrow, so a handler can tell bad data from a failed request withisServerSideDataError(ev.details). The parent stays open over an unloaded slot until the server answers correctly. - Filtering is the server's job, ancestor preservation included: a matching descendant is only reachable if the server still returns the parents above it.
- Export covers the rows the client holds, in the shape on screen: an expanded parent is followed by the children the grid has fetched, a collapsed one stands alone, and nothing is written for blocks never requested. Produce full extracts on the server.
Keyboard navigation
Tree navigation defaults to ordinary grid behavior. Hierarchy mode maps Ctrl/Cmd+Right to expand and Ctrl/Cmd+Left to collapse or move to the parent. Applications may allow users to switch modes at runtime:
const options = {
treeData: {
mode: "parent",
getParentId: (row) => row.parentId,
keyboardNavigationMode: "hierarchy",
enableKeyboardNavigationModeSwitch: true,
},
} satisfies GridOptions;
api.setKeyboardNavigationMode("grid");
// Both fields are also reconfigurable after mount (the rest of `treeData` is not):
api.setTreeDataKeyboardNavigationOptions({ enableKeyboardNavigationModeSwitch: false });
Tree data cannot be combined with value-based row grouping or pivot mode, on either row model.