Tree Operations
This page demonstrates common tree operations: adding folders and files, renaming, deleting, and expanding/collapsing nodes.
<div controller={PageController}>
<div style="margin-bottom: 16px; display: flex; gap: 8px; flex-wrap: wrap">
<Button
onClick={(e, instance) =>
instance.getControllerByType(PageController).addFolder()
}
text="Add Folder"
icon="folder"
/>
<Button
onClick={(e, instance) =>
instance.getControllerByType(PageController).addFile()
}
text="Add File"
icon="file"
/>
<Button
onClick={(e, instance) =>
instance.getControllerByType(PageController).renameSelected()
}
text="Rename"
icon="pencil"
disabled={expr(m.selection, (s) => !s)}
/>
<Button
onClick={(e, instance) =>
instance.getControllerByType(PageController).deleteSelected()
}
text="Delete"
icon="trash"
disabled={expr(m.selection, (s) => !s)}
/>
<Button
onClick={(e, instance) =>
instance.getControllerByType(PageController).expandAll()
}
text="Expand All"
/>
<Button
onClick={(e, instance) =>
instance.getControllerByType(PageController).collapseAll()
}
text="Collapse All"
/>
</div>
<Grid
records={m.data}
mod="tree"
style="height: 300px"
scrollable
keyField="id"
dataAdapter={{ type: TreeAdapter }}
selection={{ type: KeySelection, bind: m.selection, keyField: "id" }}
columns={[
{
header: "Name",
field: "name",
children: (
<TreeNode
expanded={m.$record.$expanded}
leaf={m.$record.$leaf}
level={m.$record.$level}
text={m.$record.name}
/>
),
},
]}
/>
</div> | Name |
|---|
Select a folder to add files or subfolders inside it. Select any node to rename or delete it.
Adding Nodes
To add a child node, use updateTree to find the parent and append to its $children array:
updateTree(
data,
(node) => ({
...node,
$expanded: true,
$children: [...(node.$children || []), newNode],
}),
(node) => node.id === parentId,
"$children"
);
Before adding, use findTreeNode to check if the selected node is a folder:
const selectedNode = findTreeNode(data, (n) => n.id === selectedId, "$children");
if (selectedNode?.$leaf) {
alert("Cannot add to a file. Please select a folder.");
return;
}
Removing Nodes
Use removeTreeNodes to delete nodes by ID:
removeTreeNodes(data, (node) => node.id === targetId, "$children");
This removes the node and all its descendants.
Renaming Nodes
Use updateTree to find and update a node:
updateTree(
data,
(node) => ({ ...node, name: newName }),
(node) => node.id === targetId,
"$children"
);
Expanding and Collapsing
To expand or collapse all folders, use updateTree with a predicate that matches non-leaf nodes:
// Expand all
updateTree(
data,
(node) => ({ ...node, $expanded: true }),
(node) => !node.$leaf,
"$children"
);
// Collapse all
updateTree(
data,
(node) => ({ ...node, $expanded: false }),
(node) => !node.$leaf,
"$children"
);
Preserving State Across Reloads
When tree data is reloaded from a server, expanded state is normally lost. To preserve it:
<Grid
records={m.data}
keyField="id"
dataAdapter={{
type: TreeAdapter,
restoreExpandedNodesOnLoad: true,
}}
columns={columns}
/>
The keyField is required so nodes can be matched across data updates.
See also: updateTree, findTreeNode, removeTreeNodes, Tree Grid, TreeAdapter