Angular Tree Data Grid
Angular applications can start with the free/core tree pattern by preparing a flat source array in the component and rendering indentation through a cell template.
INFO
The core tree pattern does not require Pro plugins. The current live core-tree widget is published for TypeScript, React, and Vue; Angular uses the same flattened source and cell-template approach.
Tree-like rows do not require Pro when your application only needs a visual hierarchy. The free/core approach is to flatten parent-child records into display order, add a level field, and render indentation in a cell template.
This keeps RevoGrid simple: the grid receives a normal source array, virtualization still works, and your application owns the hierarchy transformation.
Tree model
Start with records that have an ID and a parent ID:
const inputData = [
{ id: 4, parentId: 0, name: 'Antoni father for 2' },
{ id: 5, parentId: 4, name: 'Odin' },
{ id: 6, parentId: 4, name: 'John' },
{ id: 1, parentId: 0, name: 'Mary mother for 2' },
{ id: 2, parentId: 1, name: 'Howard has also 2' },
];Convert that data into a flat display list before assigning it to the grid:
function buildTreeData(rows: typeof inputData, rootParentId = 0) {
const childrenByParent = new Map<number, typeof inputData>();
for (const row of rows) {
const children = childrenByParent.get(row.parentId) ?? [];
children.push(row);
childrenByParent.set(row.parentId, children);
}
const result: Array<(typeof inputData)[number] & { level: number }> = [];
function appendChildren(parentId: number, level = 0) {
for (const child of childrenByParent.get(parentId) ?? []) {
result.push({ ...child, level });
appendChildren(child.id, level + 1);
}
}
appendChildren(rootParentId);
return result;
}Indented cell template
The core demo uses a normal cell template to show hierarchy depth:
const columns = [
{
prop: 'name',
name: 'Tree',
size: 300,
cellTemplate(h, { value, model }) {
return h(
'div',
{
style: {
marginLeft: `${(model?.level ?? 0) * 30}px`,
},
},
value || '',
);
},
},
{
prop: 'level',
readonly: true,
},
];Use the core approach when the hierarchy is read-only or mostly decorative: category outlines, simple nested lists, document sections, or a tree table where your app handles all expand/collapse logic outside the grid.
Core limits
The free/core pattern is intentionally lightweight. It does not add built-in expand/collapse controls, automatic parent-child visibility, tree-aware drag-and-drop, sticky parent rows, or parent/descendant row selection behavior. Those behaviors belong in the Pro Tree Data plugin.
Angular notes
- Build
sourcefrom your component, service, signal, or store. - Bind
[columns]and[source]normally; no Pro plugin is needed for the core tree. - Keep the tree transformation separate from the component template.
<revo-grid
[columns]="columns"
[source]="treeData"
[readonly]="true"
[canFocus]="false"
></revo-grid>Pro Tree Data
RevoGrid Pro adds a dedicated TreeDataPlugin for advanced data grid tree structures. It keeps the original source flat, computes hierarchy metadata internally, and renders tree controls in the column marked with tree: true.
RevoGrid Pro
Use the Pro plugin when hierarchy is not just indentation: users need expand/collapse controls, persistent expansion state, filtering and sorting with hierarchy awareness, row selection across descendants, drag-and-drop reparenting, or sticky parent rows while scrolling long branches.
Pro data model
The Pro model also starts from stable IDs and parent IDs:
type TeamRow = {
id: string;
parentId: string | null;
name: string;
role: string;
budget: number;
};
const source: TeamRow[] = [
{ id: 'eng', parentId: null, name: 'Engineering', role: 'Department', budget: 850000 },
{ id: 'platform', parentId: 'eng', name: 'Platform', role: 'Team', budget: 320000 },
{ id: 'api', parentId: 'platform', name: 'API', role: 'Squad', budget: 140000 },
{ id: 'design', parentId: null, name: 'Design', role: 'Department', budget: 260000 },
];Unlike the core example, you do not add level yourself. The plugin derives level, visibility, expanded state, and child metadata from the tree config.
Pro column and plugins
Mark one hierarchy column with tree: true, register TreeDataPlugin, and pass the tree config:
import {
TreeDataPlugin,
RowOrderPlugin,
RowSelectPlugin,
StickyCellsPlugin,
TREE_EXPAND_ALL_EVENT,
TREE_COLLAPSE_ALL_EVENT,
} from '@revolist/revogrid-pro';
const columns = [
{
prop: 'name',
name: 'Team',
size: 300,
tree: true,
sortable: true,
rowSelect: true,
rowDrag: true,
},
{ prop: 'role', name: 'Role', size: 160 },
{ prop: 'budget', name: 'Budget', size: 140 },
];
const plugins = [
TreeDataPlugin,
RowOrderPlugin,
RowSelectPlugin,
StickyCellsPlugin,
];
const tree = {
idField: 'id',
parentIdField: 'parentId',
rootParentId: null,
expandedRowIds: new Set(['eng']),
stickyParents: true,
};Framework wrappers pass these values as props or bindings. Plain TypeScript assigns them to the revo-grid element.
Expand and collapse controls
The Pro plugin exposes events for toolbar buttons and external controls:
grid.dispatchEvent(new CustomEvent(TREE_EXPAND_ALL_EVENT));
grid.dispatchEvent(new CustomEvent(TREE_COLLAPSE_ALL_EVENT));Use expandAll: true when every branch should open on first render. Use expandedRowIds when you want controlled initial state or persisted expansion.
Production guidance
- Keep IDs stable across refreshes so expanded state survives data updates.
- Use
parentId: nullor another explicitrootParentIdconsistently. - Keep only one tree column active; use normal columns for the rest of the row data.
- Combine tree data with virtualization for large hierarchies instead of rendering nested DOM lists.
- Add
StickyCellsPluginonly when sticky parent rows are useful for long branches. - Reconcile drag-and-drop changes in your application store before saving to the backend.
- Prefer flat server payloads for large trees; they are simpler to page, diff, validate, and persist.