How to filter tree nodes in React Tree View

4 Sep 202624 minutes to read

You can filter the tree nodes based on their text by combining the Tree View’s fields configuration with the DataManager plugin (typically by re-querying the data and replacing the bound data source whenever the search text changes).

The following code example demonstrates how to filter the tree nodes in a Tree View.

import * as ReactDOM from 'react-dom';
import * as React from 'react';
import { DataManager, Query, Predicate } from '@syncfusion/ej2-data';
import { MaskedTextBoxComponent } from "@syncfusion/ej2-react-inputs";
import { TreeViewComponent } from '@syncfusion/ej2-react-navigations';
function App() {
    // list data source for TreeView component
    let localData = [
        { id: 1, name: "Australia", hasChild: true },
        { id: 2, pid: 1, name: "New South Wales" },
        { id: 3, pid: 1, name: "Victoria" },
        { id: 4, pid: 1, name: "South Australia" },
        { id: 6, pid: 1, name: "Western Australia" },
        { id: 7, name: "Brazil", hasChild: true },
        { id: 8, pid: 7, name: "Paraná" },
        { id: 9, pid: 7, name: "Ceará" },
        { id: 10, pid: 7, name: "Acre" },
        { id: 11, name: "China", hasChild: true },
        { id: 12, pid: 11, name: "Guangzhou" },
        { id: 13, pid: 11, name: "Shanghai" },
        { id: 14, pid: 11, name: "Beijing" },
        { id: 15, pid: 11, name: "Shantou" },
        { id: 16, name: "France", hasChild: true },
        { id: 17, pid: 16, name: "Pays de la Loire" },
        { id: 18, pid: 16, name: "Aquitaine" },
        { id: 19, pid: 16, name: "Brittany" },
        { id: 20, pid: 16, name: "Lorraine" },
        { id: 21, name: "India", hasChild: true },
        { id: 22, pid: 21, name: "Assam" },
        { id: 23, pid: 21, name: "Bihar" },
        { id: 24, pid: 21, name: "Tamil Nadu" },
        { id: 25, pid: 21, name: "Punjab" }
    ];
    // Mapping TreeView fields property with data source properties
    let field = { dataSource: localData, id: 'id', parentID: 'pid', text: 'name', hasChildren: 'hasChild', expanded: "expanded" };
    let treeObj;
    let maskObj;
    //Change the dataSource for TreeView
    function changeDataSource(data) {
        treeObj.fields = {
            dataSource: data, id: 'id', text: 'name', parentID: 'pid', hasChildren: 'hasChild'
        };
    }
    //Filtering the TreeNodes
    function searchNodes(args) {
        let _text = maskObj.element.value;
        let predicats = [], _array = [], _filter = [];
        if (_text == "") {
            changeDataSource(localData);
        }
        else {
            let predicate = new Predicate('name', 'startswith', _text, true);
            let filteredList = new DataManager(localData).executeLocal(new Query().where(predicate));
            console.log(filteredList);
            for (let j = 0; j < filteredList.length; j++) {
                _filter.push(filteredList[j]["id"]);
                let filters = getFilterItems(filteredList[j], localData);
                for (let i = 0; i < filters.length; i++) {
                    if (_array.indexOf(filters[i]) == -1 && filters[i] != null) {
                        _array.push(filters[i]);
                        predicats.push(new Predicate('id', 'equal', filters[i], false));
                    }
                }
            }
            if (predicats.length == 0) {
                changeDataSource([]);
            }
            else {
                let query = new Query().where(Predicate.or(predicats));
                let newList = new DataManager(localData).executeLocal(query);
                changeDataSource(newList);
                let proxy = treeObj;
                setTimeout(function () {
                    proxy.expandAll();
                }, 100);
            }
        }
    }
    //Find the Parent Nodes for corresponding childs
    function getFilterItems(fList, list) {
        let nodes = [];
        nodes.push(fList["id"]);
        let query2 = new Query().where('id', 'equal', fList["pid"], false);
        let fList1 = new DataManager(list).executeLocal(query2);
        if (fList1.length != 0) {
            let pNode = getFilterItems(fList1[0], list);
            for (let i = 0; i < pNode.length; i++) {
                if (nodes.indexOf(pNode[i]) == -1 && pNode[i] != null)
                    nodes.push(pNode[i]);
            }
            return nodes;
        }
        return nodes;
    }
    return (<div className='control-pane'>
            <div className='control-section'>
                <div className='control_wrapper'>
                    <MaskedTextBoxComponent ref={(mask) => { maskObj = mask; }} change={searchNodes.bind(this)}/>
                    {/* Render TreeView */}
                    <TreeViewComponent fields={field} ref={(treeview) => { treeObj = treeview; }}/>
                </div>
            </div>
        </div>);
}
export default App;
ReactDOM.render(<App />, document.getElementById('sample'));
import * as ReactDOM from 'react-dom';
import * as React from 'react';
import { DataManager, Query, ReturnOption, Predicate } from '@syncfusion/ej2-data';
import { MaskedTextBoxComponent, ChangeEventArgs} from "@syncfusion/ej2-react-inputs";
import {TreeViewComponent} from '@syncfusion/ej2-react-navigations';

function App() {

    // list data source for TreeView component
    let localData: { [key: string]: Object }[] = [
        { id: 1, name: "Australia", hasChild: true },
        { id: 2, pid: 1, name: "New South Wales" },
        { id: 3, pid: 1, name: "Victoria" },
        { id: 4, pid: 1, name: "South Australia" },
        { id: 6, pid: 1, name: "Western Australia" },
        { id: 7, name: "Brazil", hasChild: true },
        { id: 8, pid: 7, name: "Paraná" },
        { id: 9, pid: 7, name: "Ceará" },
        { id: 10, pid: 7, name: "Acre" },
        { id: 11, name: "China", hasChild: true },
        { id: 12, pid: 11, name: "Guangzhou" },
        { id: 13, pid: 11, name: "Shanghai" },
        { id: 14, pid: 11, name: "Beijing" },
        { id: 15, pid: 11, name: "Shantou" },
        { id: 16, name: "France", hasChild: true },
        { id: 17, pid: 16, name: "Pays de la Loire" },
        { id: 18, pid: 16, name: "Aquitaine" },
        { id: 19, pid: 16, name: "Brittany" },
        { id: 20, pid: 16, name: "Lorraine" },
        { id: 21, name: "India", hasChild: true },
        { id: 22, pid: 21, name: "Assam" },
        { id: 23, pid: 21, name: "Bihar" },
        { id: 24, pid: 21, name: "Tamil Nadu" },
        { id: 25, pid: 21, name: "Punjab" }
    ];

    // Mapping TreeView fields property with data source properties
    let field:Object = { dataSource: localData, id: 'id', parentID: 'pid', text: 'name', hasChildren: 'hasChild', expanded: "expanded" }
    let treeObj: TreeViewComponent;
    let maskObj: MaskedTextBoxComponent;

   //Change the dataSource for TreeView
    function changeDataSource(data: any) {
        treeObj.fields = {
            dataSource: data, id: 'id', text: 'name',parentID: 'pid', hasChildren: 'hasChild'
        }
    }

    //Filtering the TreeNodes
     function searchNodes(args: ChangeEventArgs) {
        let _text = maskObj.element.value;
        let predicats = [], _array = [], _filter = [];
        if (_text == "") {
            changeDataSource(localData);
        }
        else {
            let predicate = new Predicate('name', 'startswith', _text, true);
            let filteredList : any = new DataManager(localData).executeLocal(new Query().where(predicate));
            console.log(filteredList)
            for (let j = 0; j < filteredList.length; j++) {
                _filter.push(filteredList[j]["id"]);
                let filters = getFilterItems(filteredList[j], localData);
                for (let i = 0; i < filters.length; i++) {
                    if (_array.indexOf(filters[i]) == -1 && filters[i] != null) {
                        _array.push(filters[i]);
                        predicats.push(new Predicate('id', 'equal', filters[i], false));
                    }
                }
            }
            if (predicats.length == 0) {
                changeDataSource([]);
            } else {
                let query = new Query().where(Predicate.or(predicats));
                let newList : any =new DataManager(localData).executeLocal(query);
                changeDataSource(newList);
                let proxy: any = treeObj as TreeViewComponent;
                setTimeout(function (this: any) {
                    proxy.expandAll();
                }, 100);
            }
        }
    }

    //Find the Parent Nodes for corresponding childs
    function getFilterItems(fList: any, list: any): any {
        let nodes = [];
        nodes.push(fList["id"]);
        let query2 = new Query().where('id', 'equal', fList["pid"], false);
        let fList1 = new  DataManager(list).executeLocal(query2);
        if (fList1.length != 0) {
            let pNode:any= getFilterItems(fList1[0], list);
            for (let i = 0; i < pNode.length; i++) {
                if (nodes.indexOf(pNode[i]) == -1 && pNode[i] != null)
                    nodes.push(pNode[i]);
                }
                return nodes;
        }
            return nodes;
    }

    return (
        <div className = 'control-pane'>
            <div className='control-section'>
                <div className='control_wrapper'>
                    <MaskedTextBoxComponent ref={(mask) => { maskObj = mask as MaskedTextBoxComponent; }} change={searchNodes.bind(this)} />
                    {/* Render TreeView */}
                    <TreeViewComponent fields={field}  ref={(treeview) => { treeObj = treeview as TreeViewComponent; }}/>
                </div>
            </div>
        </div>
    )
}
export default App;
ReactDOM.render(<App />, document.getElementById('sample'));
<!DOCTYPE html>
<html lang="en">

<head>
    <title>Syncfusion React TreeView</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="Essential JS 2 for React Components" />
    <meta name="author" content="Syncfusion" />
    <link href="style.css" rel="stylesheet"/>
    <link href="https://cdn.syncfusion.com/ej2/34.2.2/ej2-base/styles/tailwind3.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/34.2.2/ej2-inputs/styles/tailwind3.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/34.2.2/ej2-buttons/styles/tailwind3.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/34.2.2/ej2-react-navigations/styles/tailwind3.css" rel="stylesheet" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
    <script src="systemjs.config.js"></script>
</head>

<body>
    <div id='treeparent'>
        <div id='sample'>
            <div id='loader'>Loading....</div>
        </div>
    </div>
</body>

</html>
#loader {
    color: #008cff;
    height: 40px;
    left: 45%;
    position: absolute;
    top: 45%;
    width: 30%;
}

#treeparent {
    display: block;
    max-width: 225px;
    max-height: 350px;
    margin: auto;
    overflow: auto;
    border: 1px solid #dddddd;
    border-radius: 3px;
}
.treeviewfilter {
    width: 225px;
	min-height: 250px;
	max-height: auto;
	border: 1px solid #bbbbbb;
	margin: 0 auto;
}
.e-mask .e-input {
	border: none;
	border-bottom: 1px solid #bbbbbb;
}