Tree View
The tree view renders a hierarchical structure from a flat list of records. It uses an id/parentId approach — you return all nodes as a flat array and the UI assembles the tree client-side.
Spec
new TreeView()
.endpoint({ method: 'GET', url: '/departments' })
.idField('id')
.parentField('parentId')
.labelField('name')
.metadata({ title: 'Department Structure' })
.build()How it works
The SPA navigates to #/{resource}/tree and fetches a TreeSpec from GET /api/ui/{resource}/tree. The spec describes how to fetch the flat node list and which fields to use for assembling the hierarchy.
Then the SPA fetches all nodes from spec.endpoint and builds the tree by matching each node's parentField value to another node's idField value. Root nodes are those with a null or missing parent.
Basic setup (JS)
import { TreeView } from '@retrofit-ui/builder-zod';
// Each row must have: id, parentId (or null for roots), name
// URL prefix below is arbitrary — pick anything and match `apiBase` to it.
app.get('/files/departments/tree', (_req, res) => {
res.json(
new TreeView()
.endpoint({ method: 'GET', url: '/departments' })
.idField('id')
.parentField('parentId')
.labelField('name')
.metadata({ title: 'Department Structure' })
.build(),
);
});Your /departments endpoint returns a flat array:
[
{ "id": 1, "name": "Engineering", "parentId": null },
{ "id": 2, "name": "Frontend", "parentId": 1 },
{ "id": 3, "name": "Alice Johnson", "parentId": 2 },
{ "id": 4, "name": "Bob Martinez", "parentId": 2 },
{ "id": 5, "name": "Backend", "parentId": 1 },
{ "id": 6, "name": "Design", "parentId": null }
]Spec fields
| Field | Type | Required | Description |
|---|---|---|---|
endpoint | EndpointDirective | ✓ | Fetches the flat node array |
idField | string | ✓ | Field on each node that uniquely identifies it (default: 'id') |
parentField | string | ✓ | Field referencing the parent's ID; null/missing = root (default: 'parentId') |
labelField | string | ✓ | Field to display as the node label (default: 'name') |
selection | 'single' | 'multiple' | 'leaf' | — | Selection mode (default: 'single') |
actions | { create?, update?, delete? } | — | CRUD buttons |
metadata.title | string | — | Heading above the tree |
Selection modes
'single''multiple''leaf'Spec
new TreeView()
.endpoint({ method: 'GET', url: '/employees' })
.idField('id')
.parentField('managerId')
.labelField('name')
.selection('multiple')
.build();CRUD actions
Add .create(), .update(), and .delete() to enable action buttons:
new TreeView()
.endpoint({ method: 'GET', url: '/departments' })
.idField('id')
.parentField('parentId')
.labelField('name')
.create({ method: 'POST', url: '/departments' })
.update({ method: 'PUT', url: '/departments/{id}' })
.delete({ method: 'DELETE', url: '/departments/{id}' })
.build();| Action | Button | Behaviour |
|---|---|---|
create | New (top-right) | Navigates to #/{resource}/new |
update | Edit | Navigates to #/{resource}/{selectedId}; disabled when ≠ 1 item selected |
delete | Delete | Opens a confirmation dialog, then calls the endpoint for each selected ID |
The delete action is the only one handled inline — it prompts with a confirmation dialog before calling the endpoint, then shows a success or error toast.
Linking from a table
Use a table row action to navigate users to the tree view:
TableView.schema(ProjectSchema)
.rowAction({ label: 'Structure', routePattern: '/tree' })
// note: routes to #/{resource}/tree, not per-entity
.list({ method: 'GET', url: '/projects' })
.build();