CxJS

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>
NameCity
North America
West Coast
Carol White
Seattle
David Brown
Portland
Alice Johnson
New York
Bob Smith
Los Angeles
Europe
Eva Green
London
Frank Miller
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:

  1. Show a node if it matches the search
  2. Show a parent node if any descendant matches (even if the parent doesn’t match)
  3. 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 search
  • predicate - Function that returns true when a match is found
  • childrenField - 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

PropertyTypeDescription
filterParamsPropSearch parameters passed to onCreateFilter.
onCreateFilterfunctionReturns a predicate (node) => boolean for filtering.
emptyTextstringText shown when no nodes match the filter.