Searching Tree Grids
import { findTreeNode } from 'cx/data'; Copied Tree grids require special handling for search because a parent node should remain visible if any of its descendants match the search query.
<div controller={PageController}>
<TextField
value={m.search}
icon="search"
placeholder="Search..."
style="margin-bottom: 16px"
/>
<Grid
records={m.data}
mod="tree"
style="height: 350px"
scrollable
dataAdapter={{ type: TreeAdapter }}
emptyText="No records match the search"
filterParams={m.search}
onCreateFilter={(search: string) => {
if (!search) return () => true
search = search.toLowerCase()
return (node: TreeRecord) => {
if (isMatch(node, search)) return true
if (node.$leaf || !node.$children) return false
const result = findTreeNode(
node.$children,
(subNode: TreeRecord) => isMatch(subNode, search),
"$children",
)
return result ? true : false
}
}}
columns={[
{
header: "Name",
field: "name",
children: (
<TreeNode
expanded={m.$record.$expanded}
leaf={m.$record.$leaf}
level={m.$record.$level}
text={m.$record.name}
/>
),
},
{ header: "City", field: "city" },
]}
/>
</div> | Name | City |
|---|---|
| Seattle | |
| Portland | |
| New York | |
| Los Angeles | |
| London | |
| Paris |
Try searching for “Seattle” or “Europe” to see how parent nodes remain visible when children match.
How It Works
When filtering a flat grid, you simply check if each record matches. With trees, the logic is:
- Show a node if it matches the search
- Show a parent node if any descendant matches (even if the parent doesn’t match)
- Hide leaf nodes that don’t match
The findTreeNode utility recursively searches through a tree to find matching descendants. It takes three arguments:
nodes- Array of nodes to searchpredicate- Function that returnstruewhen a match is foundchildrenField- Name of the property containing child nodes (default:"$children")
Returns the first matching node or null if no match is found.
Performance Considerations
This approach calls the filter function for each node in the tree. For each non-leaf node, it may also traverse the entire subtree to find matches.
For very large trees (thousands of nodes), consider:
- Server-side filtering that returns only matching branches
- Caching search results
- Debouncing the search input
Configuration
| Property | Type | Description |
|---|---|---|
filterParams | Prop | Search parameters passed to onCreateFilter. |
onCreateFilter | function | Returns a predicate (node) => boolean for filtering. |
emptyText | string | Text shown when no nodes match the filter. |