This is a migrated thread and some comments may be shown as answers.

Filter kendo-ui TreeView with input filter and category

15 Answers 1035 Views
Wrappers for React
This is a migrated thread and some comments may be shown as answers.
Vincent
Top achievements
Rank 1
Vincent asked on 10 Jul 2018, 12:20 PM

Hi,

I'm trying to filter a kendo-ui treeView by text and with button to display or not some root in my tree.

I already use this to filter the tree view with input and it work perfectly, and on top of that i want to filter the root node.

I use treeview react component

When i click some button, I set the hidden attribute to the root node and I re-filter it.

public handleClick() {
  const dataSource: kendo.data.DataSource = $("[data-role='treeview']").data("kendoTreeView").dataSource;
  const data = dataSource instanceof kendo.data.HierarchicalDataSource && dataSource.data();
  _.forEach(data as kendo.data.ObservableArray, (item) => {
    if (item.type === this.props.controlNode) {
      item.hidden = this.props.filter;
    }
  });
  dataSource.filter({ field: "hidden", operator: "neq", value: true });
  dataSource.filter().logic = "and";
}


My buttons have a `controlNode` attribute to compare to the type attribute inside my dataSource. I iterate through the first child of my dataSource and if the type match I set the hidden attribute true or false depending on the filter state.

So my problem is, `dataSource.filter({ field: "hidden", operator: "neq", value: true });` don't filter the dataSource. but if i use the operator `eq` instead of `neq` I have the opposite behavior, but it's working...

I don't know if I'm doing something wrong.

15 Answers, 1 is accepted

Sort by
0
Stefan
Telerik team
answered on 11 Jul 2018, 07:25 AM
Hello, Vincent,

Thank you for all of the details.

Based on the code the approach should be working.

As this seems as a strange behavior could you share an example with us reproducing the issue and we will be happy to investigate it.

If sharing an example is currently, not possible please check if reversing the logic will lead to the expected result. For example, if "eq" operator is working, but it is showing the opposite results, I can suggest to either change the filter value to false instead of true or to set "!this.props.filter" instead of "this.props.filter".

I hope one of the approaches to help reserve the logic and achieve the desired result.

Regards,
Stefan
Progress Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Vincent
Top achievements
Rank 1
answered on 11 Jul 2018, 09:39 AM

Hello, Stefan,

I tried reverse the logic and didn't work.

I build a little setup, join here.

I just have a little bug, the 

$("[data-role='treeview']").data("kendoTreeView")

return undefined. I have no idea why, since it's working perfectly fine in app.

Thank,
Vincent.

 

import React from "react";
import * as ReactDOM from "react-dom";
import _ from "lodash";
import $ from "jquery";
 
import "@progress/kendo-theme-default/dist/all.css";
import { TreeView } from "@progress/kendo-treeview-react-wrapper";
import { IconButton, TextField } from "@material-ui/core";
 
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { library } from "@fortawesome/fontawesome-svg-core";
import { faGlobe, faUser, faCube } from "@fortawesome/free-solid-svg-icons";
 
enum TreeType {
    AccountRoot = "AccountRoot",
    Account = "Account",
    PackageRoot = "PackageRoot",
    Package = "Package",
    EnvironmentRoot = "EnvironmentRoot",
    Environment = "Environment",
}
 
interface ITreeViewNode {
    id: string;
    text: string;
    type: TreeType;
    expanded?: boolean;
    items?: ITreeViewNode[];
}
 
interface IDemoProps {
    data: ITreeViewNode[];
}
 
function filterTreeView(dataSource: kendo.data.DataSource, query: string): boolean {
    let hasVisibleChildren = false;
    const data = dataSource instanceof kendo.data.HierarchicalDataSource && dataSource.data();
    _.forEach(data as kendo.data.ObservableArray, (item) => {
        const text: string = item.text.toLowerCase();
        const itemVisible =
            query === "" // query is empty
            || text.indexOf(query as string) >= 0; // item text matches query
 
        const anyVisibleChildren = filterTreeView(item.children, query); // pass true if parent matches
 
        hasVisibleChildren = hasVisibleChildren || anyVisibleChildren || itemVisible;
 
        item.hidden = !itemVisible && !anyVisibleChildren;
    });
 
    if (data) {
        // re-apply filter on children
        dataSource.filter({ field: "hidden", operator: "neq", value: true });
    }
    return hasVisibleChildren;
}
 
class Demo extends React.Component<IDemoProps> {
 
    public state = {
        userStateFilter : false,
        packageStateFilter : false,
        environmentStateFilter : false,
    };
 
    public constructor(props: IDemoProps) {
        super(props);
 
        this.onSelect = this.onSelect.bind(this);
        this.handleTextFilter = this.handleTextFilter.bind(this);
        this.handleClickUser = this.handleClickUser.bind(this);
        this.handleClickPackage = this.handleClickPackage.bind(this);
        this.handleClickEnvironment = this.handleClickEnvironment.bind(this);
    }
 
    public onSelect = (e: kendo.ui.TreeViewSelectEvent) => {
        if (e.node !== undefined) {
            const treeView = $("[data-role='treeview']").data("kendoTreeView");
            treeView.toggle(e.node);
            // @ts-ignore : custom attribute (type)
            console.log(treeView.dataItem(e.node).id + "  :  " + treeView.dataItem(e.node).type);
        }
    }
 
    public handleTextFilter(e: React.ChangeEvent<HTMLInputElement>) {
        const treeData = $("[data-role='treeview']").data("kendoTreeView");
        console.log(treeData);
        // const value = e.target.value.toLocaleLowerCase();
        // filterTreeView(treeData, value);
    }
 
    public handleClickUser() {
        this.state.userStateFilter = !this.state.userStateFilter;
        const dataSource: kendo.data.DataSource = $("[data-role='treeview']").data("kendoTreeView").dataSource;
        const data = dataSource instanceof kendo.data.HierarchicalDataSource && dataSource.data();
        _.forEach(data as kendo.data.ObservableArray, (item) => {
            if (item.type === TreeType.AccountRoot) {
                item.hidden = this.state.userStateFilter;
            }
        });
        dataSource.filter({ field: "hidden", operator: "neq", value: true });
        dataSource.filter().logic = "and";
    }
 
    public handleClickPackage() {
        this.state.packageStateFilter = !this.state.packageStateFilter;
        const dataSource: kendo.data.DataSource = $("[data-role='treeview']").data("kendoTreeView").dataSource;
        const data = dataSource instanceof kendo.data.HierarchicalDataSource && dataSource.data();
        _.forEach(data as kendo.data.ObservableArray, (item) => {
            if (item.type === TreeType.PackageRoot) {
                item.hidden = this.state.packageStateFilter;
            }
        });
        dataSource.filter({ field: "hidden", operator: "neq", value: true });
        dataSource.filter().logic = "and";
    }
 
    public handleClickEnvironment() {
        this.state.environmentStateFilter = !this.state.environmentStateFilter;
        const dataSource: kendo.data.DataSource = $("[data-role='treeview']").data("kendoTreeView").dataSource;
        const data = dataSource instanceof kendo.data.HierarchicalDataSource && dataSource.data();
        _.forEach(data as kendo.data.ObservableArray, (item) => {
            if (item.type === TreeType.EnvironmentRoot) {
                item.hidden = this.state.environmentStateFilter;
            }
        });
        dataSource.filter({ field: "hidden", operator: "neq", value: true });
        dataSource.filter().logic = "and";
    }
 
    public render() {
        return (
            <div>
                <TextField id="input-search" label="Search" onChange={this.handleTextFilter} />
                <IconButton color="inherit" onClick={this.handleClickUser}>
                    <FontAwesomeIcon icon="user" size="xs"/>
                </IconButton>
                <IconButton color="inherit" onClick={this.handleClickPackage}>
                    <FontAwesomeIcon icon="cube" size="xs"/>
                </IconButton>
                <IconButton color="inherit" onClick={this.handleClickEnvironment}>
                    <FontAwesomeIcon icon="globe" size="xs"/>
                </IconButton>
                <TreeView
                    loadOnDemand={false}
                    select={this.onSelect}
                    dataSource={this.props.data}/>
            </div>
        );
    }
}
 
class App extends React.Component {
 
    public data: ITreeViewNode[] = [
        {
            id: "UserRoot",
            text: "UserRoot",
            type: TreeType.AccountRoot,
            expanded: false,
            items: [{
                id: "User1",
                text: "User1",
                type: TreeType.Account,
            },
            {
                id: "User2",
                text: "User2",
                type: TreeType.Account,
            }],
        },
        {
            id: "PackageRoot",
            text: "PackageRoot",
            type: TreeType.PackageRoot,
            expanded: false,
            items: [{
                id: "Package1",
                text: "Package1",
                type: TreeType.Package,
            },
            {
                id: "Package2",
                text: "Package2",
                type: TreeType.Package,
            }],
        },
        {
            id: "EnvironmentRoot",
            text: "EnvironmentRoot",
            type: TreeType.EnvironmentRoot,
            expanded: false,
            items: [{
                id: "Environment1",
                text: "Environment1",
                type: TreeType.Environment,
            },
            {
                id: "Environment2",
                text: "Environment2",
                type: TreeType.Environment,
            }],
        },
    ];
 
    public render() {
        return (
            <Demo
                data={this.data}
            />
        );
    }
}
 
library.add(faGlobe);
library.add(faUser);
library.add(faCube);
 
ReactDOM.render(
    <App />,
    document.getElementById("app"),
);
0
Vincent
Top achievements
Rank 1
answered on 11 Jul 2018, 01:07 PM

Here a little demo : https://i.imgur.com/wBaJWj7.gifv

0
Stefan
Telerik team
answered on 12 Jul 2018, 07:28 AM
Hello, Vincent,

Thank you for the code.

After inspecting the case I think I noticed that the recursion is not used.

In our demo, notice how the filter function is called recursively in order to filter the children as well:

var anyVisibleChildren = filter(item.children, itemVisible || query); // pass true if parent matches

Currently, the used logic is only searching inside the parent elements.

Also, I used the inverted logic, setting in which nodes are not hidden and only showing them:

https://next.plnkr.co/edit/vLwgkkicwzC7bTNC

If the issue still occurs inside the real application, please share a runnable version of it so I can closely invest it.

Regards,
Stefan
Progress Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Vincent
Top achievements
Rank 1
answered on 12 Jul 2018, 08:42 AM

Hello, Stephan.

Thank you for your response.

I didn't use recursivity because with the filter "neq" I thought I will not need it.

With the logic use in your Plunker, it hide all the child. I don't really want that. 
So, I guess, I have to filter all the child and set the "hidden" field to "false".

I'll use this logic, but why the "neq" operator didn't work properly in the first place ? Is it a bug ? Or the logic use is not ok ?

Regards

Vincent.

0
Stefan
Telerik team
answered on 13 Jul 2018, 06:42 AM
Hello, Vincent,

I can assume that it is not working due to the way the not equal operator is working with true and false.

I made some test but it was working correctly in the standard cases.

As this is indeed not expected and event working in our example, we will make additional tests and log an issue to fix it if needed.

Thank you for the collaboration.

Regards,
Stefan
Progress Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Accepted
Stefan
Telerik team
answered on 13 Jul 2018, 08:22 AM
Hello, Vincent,

After more test, we located the reason for this results.

The reason is the child nodes, as they are not filtered with this condition, and as they are visible, even if the parent has to be hidden, it will be shown in order to show the child elements. If you have even one visible child element the parent element has to be visible as well.

This is why it is working in our demo as there the child nodes are filtered as well using recursion.

If the recursion filtering is implemented in the real application, then the approach with not equal should works as expected as well.

Let me know if you need more details on why this occurs.

Regards,
Stefan
Progress Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Heythem
Top achievements
Rank 1
answered on 25 Jan 2019, 09:17 AM
Hi Stephan , 
I tried to use the logic in this article https://docs.telerik.com/kendo-ui/controls/navigation/treeview/how-to/filtering/filter-out-search-results
but I keep getting an error "Uncaught TypeError: filter is not a function" caused by the recursive call within my function 

Could you please give me a suggestion or a solution in order to be able to filter a treeView based on the values selected by the user from a multi select field (means the input is not a single text but an array of values ) 
thanks a lot 

0
Stefan
Telerik team
answered on 25 Jan 2019, 12:53 PM
Hello,

I will be happy to assist in resolving the issue.

Could you please share with us how the code looks like and on which line the error occurs.

If the error occurs on this line, please ensure that the filter function is correctly bound to "this" as otherwise it may not be recognized as a function:

var anyVisibleChildren = filter(item.children, itemVisible || query); // pass true if parent matches

filter = (dataSource, query) => {
     var hasVisibleChildren = false;
     var data = dataSource instanceof kendo.data.HierarchicalDataSource && dataSource.data();
 
     for (var i = 0; i < data.length; i++) {
       var item = data[i];
       var text = item.text.toLowerCase();
       var itemVisible =
           query === true // parent already matches
           || query === "" // query is empty
           || text.indexOf(query) >= 0; // item text matches query
 
       var anyVisibleChildren = this.filter(item.children, itemVisible || query); // pass true if parent matches
 
       hasVisibleChildren = hasVisibleChildren || anyVisibleChildren || itemVisible;
 
       item.hidden = !itemVisible && !anyVisibleChildren;
     }


Regards,
Stefan
Progress Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Heythem
Top achievements
Rank 1
answered on 28 Jan 2019, 12:34 PM
Thank for your suggestion Stephan , I was able to find the solution to the multiple filters issue 

Now the only thing left is that when I try to filter using a value in the 2nd or 1st level , only that node is show the children are hidden , i dont know what's causing this behavior , because when i filter on one of the children it displays the upper nodes
0
Stefan
Telerik team
answered on 29 Jan 2019, 08:44 AM
Hello,

I`m glad that there is progress on this filtering functionality.

As for the new issue, could you please share the filter implementation or a full example, so we can observe what could be causing this?

I can assume that the recursion logic is removing the children of the filtered item as well, but observing the implementation we give as a hint why this is happening.

Regards,
Stefan
Progress Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Tamilarasan
Top achievements
Rank 1
answered on 17 Apr 2020, 06:55 AM

Hi Stefan,

I am going to use a filter, where it will be in a tree structure, from that tree list i will choose the data. similar to JS tree in jquery. attached an image as how im expecting. Is it possible to use that in kendo react grid?

0
Stefan
Telerik team
answered on 17 Apr 2020, 01:31 PM

Hello, Tamilarasan,

If there will be tree structure we recommend our TreeList component, as it is very similar to the Grid functionalities, but it is showing tree data:

https://www.telerik.com/kendo-react-ui/components/treelist/

The screenshot is also showing a component that looks like our TreeView component, so that could also be a good option:

https://www.telerik.com/kendo-react-ui/components/treeview/

Regards,
Stefan
Progress Telerik

Progress is here for your business, like always. Read more about the measures we are taking to ensure business continuity and help fight the COVID-19 pandemic.
Our thoughts here at Progress are with those affected by the outbreak.
0
Tamilarasan
Top achievements
Rank 1
answered on 17 Apr 2020, 02:50 PM
Thanks for your suggestion stefan. But integrating tree filter in grid as same as like as other filtering, also exactly like the image which i shared is not possible in grid.
0
Stefan
Telerik team
answered on 20 Apr 2020, 02:02 PM

Hello, Tamilarasan,

The Grid is designed to be used with tabular data and shows a tree only when grouped, but that is a little different type of tree.

This is why we have the TreeList and TreeView components.

The component from the screenshot is nearly 100% similar to the TreeView.

Is there any reason why the TreeList or the TreeView are not possible in this case?

Regards,
Stefan
Progress Telerik

Progress is here for your business, like always. Read more about the measures we are taking to ensure business continuity and help fight the COVID-19 pandemic.
Our thoughts here at Progress are with those affected by the outbreak.
Tags
Wrappers for React
Asked by
Vincent
Top achievements
Rank 1
Answers by
Stefan
Telerik team
Vincent
Top achievements
Rank 1
Heythem
Top achievements
Rank 1
Tamilarasan
Top achievements
Rank 1
Share this question
or