Filter kendo-ui TreeView with input filter and category

16 posts, 1 answers
  1. Vincent
    Vincent avatar
    15 posts
    Member since:
    Mar 2014

    Posted 10 Jul 2018 Link to this post

    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.

  2. Stefan
    Admin
    Stefan avatar
    2960 posts

    Posted 11 Jul 2018 Link to this post

    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
  3. Vincent
    Vincent avatar
    15 posts
    Member since:
    Mar 2014

    Posted 11 Jul 2018 in reply to Stefan Link to this post

    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"),
    );
  4. Vincent
    Vincent avatar
    15 posts
    Member since:
    Mar 2014

    Posted 11 Jul 2018 in reply to Vincent Link to this post

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

  5. Stefan
    Admin
    Stefan avatar
    2960 posts

    Posted 12 Jul 2018 Link to this post

    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
  6. Vincent
    Vincent avatar
    15 posts
    Member since:
    Mar 2014

    Posted 12 Jul 2018 in reply to Stefan Link to this post

    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.

  7. Stefan
    Admin
    Stefan avatar
    2960 posts

    Posted 13 Jul 2018 Link to this post

    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
  8. Answer
    Stefan
    Admin
    Stefan avatar
    2960 posts

    Posted 13 Jul 2018 Link to this post

    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
  9. Heythem
    Heythem avatar
    8 posts
    Member since:
    Aug 2018

    Posted 25 Jan 2019 in reply to Stefan Link to this post

    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 

  10. Stefan
    Admin
    Stefan avatar
    2960 posts

    Posted 25 Jan 2019 Link to this post

    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
  11. Heythem
    Heythem avatar
    8 posts
    Member since:
    Aug 2018

    Posted 28 Jan 2019 in reply to Stefan Link to this post

    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
  12. Stefan
    Admin
    Stefan avatar
    2960 posts

    Posted 29 Jan 2019 Link to this post

    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
  13. Tamilarasan
    Tamilarasan avatar
    3 posts
    Member since:
    Mar 2020

    Posted 17 Apr Link to this post

    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?

  14. Stefan
    Admin
    Stefan avatar
    2960 posts

    Posted 17 Apr Link to this post

    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.
  15. Tamilarasan
    Tamilarasan avatar
    3 posts
    Member since:
    Mar 2020

    Posted 17 Apr in reply to Stefan Link to this post

    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.
  16. Stefan
    Admin
    Stefan avatar
    2960 posts

    Posted 20 Apr Link to this post

    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.
Back to Top