@oge-ui/tree-list
v0.13.0
Published
Signal-based Angular tree list (hierarchical data grid): lazy loading, virtualization, editing, selection and Excel export, sharing the @oge-ui/grid column API.
Maintainers
Readme
@oge-ui/tree-list
Hierarchical data grid (tree list) for Angular, built on the same engine as
@oge-ui/grid: shared column
model, theming, virtualization, keyboard navigation and data layer.
- Flat self-referencing data (
id/parentId) or nested payloads (itemsExpr) - O(visible) expand/collapse; 100k-node trees stay smooth with
virtualScroll - Lazy child loading against any
DataSource(oneparentId eq keyrequest per expansion); filters/search on lazy trees additionally discover matches under never-expanded branches and complete their ancestor chains remotely - Column virtualization (
columnRenderingMode: 'virtual') for very wide trees - Client-side filtering & search that keep ancestor rows visible — with
expandNodesOnFiltering, per-column operator menu, a filter builder ([(filterValue)]) and<mark>search highlighting - Excel-style header filter popups (distinct values with search; date columns group by year with tri-state checkboxes), client-side paging over the visible rows
- Recursive selection cascades into lazily loaded branches by bulk-fetching
the missing subtree first (
parentId in [...]) - Column chooser, drag-and-drop column reordering, row & header context menus,
[ogeToolbar]slot,commandButtons,loadPanel,wordWrap - Selection (single / multiple / checkbox / recursive tri-state), full treegrid ARIA, RTL-aware keyboard
- Editing in all five modes (cell / row / batch / form / popup) with the grid's
editors, validators and
savingChangesflow;formItems/formColCountform layouts;addRow(parentKey)inserts under a chosen node with aninitNewRowprefill hook - Cancelable
rowExpanding/rowCollapsingevents;autoNavigateToFocusedRowexpands and scrolls to a programmatically focused row;forEachNode()/getVisibleRows()node APIs;allowSelectAlltoggle - Drag & drop: reparent by dropping onto a row, or reorder among siblings by dropping before/after (with drop indicators)
- State persistence (
stateKey) for sort, filters, column layout and expansion - CSV export with first-column indentation; Excel export
(
@oge-ui/tree-list/export-excel, lazy) with native spreadsheet outlining
Install
npm i @oge-ui/tree-list @oge-ui/grid @oge-ui/coreQuick start
import { OgeTreeList, OgeColumn } from '@oge-ui/tree-list';
@Component({
imports: [OgeTreeList, OgeColumn],
template: `
<oge-tree-list [data]="tasks" keyExpr="id" parentIdExpr="parentId" [autoExpandAll]="true">
<oge-column field="title" />
<oge-column field="owner" />
<oge-column field="progress" dataType="number" />
</oge-tree-list>
`,
})
export class TasksPage {
tasks = [
{ id: 1, parentId: null, title: 'Planning', owner: 'Ada', progress: 80 },
{ id: 2, parentId: 1, title: 'Requirements', owner: 'Grace', progress: 100 },
];
}<oge-column>, cell/header templates, themes and provideOgeGridConfig are the
grid's own building blocks — one shared configuration drives both components.
Lazy loading (remote children)
Give it a DataSource plus hasItemsExpr; children are fetched per expansion
with filter: [parentIdExpr, '=', parentKey] (the root load uses rootValue):
<oge-tree-list [data]="source" keyExpr="id" parentIdExpr="parentId" hasItemsExpr="hasSubordinates" />The server sees a plain filter — an OData backend works out of the box
($filter=parentId eq 42). A sort change invalidates the child cache; the
active sort is repeated on every child request. Lazy mode requires a string
parentIdExpr.
Filtering
filterRow and searchPanel run client-side over the loaded rows — the
DataSource never receives filter/search, so ancestors of matches always stay
visible. filterMode controls the visible set: 'withAncestors' (default,
matches + ancestor chain) or 'fullBranch' (also all descendants of matches).
State persistence
<oge-tree-list [data]="rows" keyExpr="id" stateKey="tasks-tree" />Sort, filters, column layout and the expansion state round-trip through
OGE_STATE_STORAGE (default: localStorage; pluggable with any async backend).
For full control use state() / applyState() and the stateChange output.
Imperative API
- Expansion & navigation —
expandAll(),collapseAll(),expandRow(key),collapseRow(key),isRowExpanded(key),scrollToRow(key | index),focusRow(key)/navigateToRow(key)(expands the ancestor path) - Data access —
getNodeByKey(key),getVisibleRows(),forEachNode(cb) - Selection —
selectAll(),deselectAll(),clearSelection(),isRowSelected(key),getSelectedRowKeys(mode),getSelectedRowsData(mode),copyToClipboard() - Editing —
addRow(parentKey?),editRow(key),deleteRow(key),saveChanges(),discardChanges(),hasChanges() - Paging & loading —
pageIndex(writable signal),setPageIndex(i),pageSize(),setPageSize(n),pageCount(),totalCount(),beginCustomLoading(message?),endCustomLoading() - State & export —
refresh(),clearFilters(),clearSorting(),state(),applyState(),getExportData(),getCsv(),exportCsv()
Migrating from other data grids
How the DevExtreme TreeList callbacks map onto this component:
| DevExtreme | OGE |
| ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| onRowClick / onRowDblClick / onCellClick / onCellDblClick | same names → flat payloads with row/key/field/value/event |
| onRowExpanding/-ed, onRowCollapsing/-ed | same names; -ing events cancelable (UI-driven toggles; the imperative API stays silent) |
| onSelectionChanged | (selectionChanged) → { selectedKeys, addedKeys, removedKeys } + [(selectedKeys)] |
| onFocusedRowChanged | (focusedRowChanged) → { key, row } + [(focusedRowKey)] |
| onEditingStart / onInitNewRow | (editingStart) (cancelable) / (initNewRow) → { key, parentKey, values } |
| onRowInserting/-ed, onRowUpdating/-ed, onRowRemoving/-ed | same names; -ing events cancelable |
| onSaving / onSaved / onEditCanceled | (savingChanges) / (savedChanges) / (editCanceled) |
| onExporting / onDataErrorOccurred | (exporting) → { fileName, cancel } / (dataErrorOccurred) → { error } |
| onNodesInitialized / lifecycle callbacks | not replicated — signals, effect() and Angular lifecycle cover them |
Theming
The shared theme files ship with @oge-ui/grid and style both components:
@import '@oge-ui/grid/themes/dark.css';For AI coding assistants
The complete machine-readable API reference ships inside the package at
node_modules/@oge-ui/tree-list/llms.txt — conventions, every documented member and
copy-pasteable demos in one file. Online: https://ogeui.com/llms.txt (index) and
https://ogeui.com/llms-full.txt (the whole suite).
