Enum
Enum

Reputation: 184

How to search for all nodes in a sap.m.Tree?

I'm currently working at a MasterDetail application for my company, which provides expandable categorys represented as nodes.

enter image description here

The binding of the nodes and it's child nodes with a navigation property isn't a problem. However, if I want to search for a certain group node in the search field above, it only filters between the four highest nodes. It can search for the nodes on the first level, but it isn't able to find nodes if they're below the first level.

Binding of the tree:

<Tree
    selectionChange="onSelectionChange"
    id="list"
    noDataText="{masterView>/noDataText}"
    busyIndicatorDelay="{masterView>/delay}"
    items="{path: '/GroupNodes',
                parameters : {
                expand: 'ChildGroupNodes',
                navigation: {
                    'GroupNodes': 'ChildGroupNodes'
                    }
            }
    }">
    <StandardTreeItem 
        title="{Stext}"
        type="Navigation"
        press="onSelectionChange"/>
</Tree>

onSearch:

    onSearch: function(oEvent) {
        if (oEvent.getParameters().refreshButtonPressed) {
            this.onRefresh();
            return;
        }

        var sQuery = oEvent.getParameter("query");
        if (sQuery) {
            this._oListFilterState.aSearch = [new Filter("Stext", FilterOperator.Contains, sQuery)];
        } else {
            this._oListFilterState.aSearch = [];
        }
        this._applyFilterSearch();
    },

_applyFilterSearch:

    _applyFilterSearch: function() {
        var aFilters = this._oListFilterState.aSearch.concat(this._oListFilterState.aFilter),
            oViewModel = this.getModel();

        this._oList.getBinding("items").filter(aFilters, "Application");
        
        if (aFilters.length !== 0) {
            oViewModel.setProperty("/noDataText", this.getResourceBundle().getText("masterListNoDataWithFilterOrSearchText"));
        } else if (this._oListFilterState.aSearch.length > 0) {
            oViewModel.setProperty("/noDataText", this.getResourceBundle().getText("masterListNoDataText"));
        }
    },

Filterstate in the onInit() function:

this._oListFilterState = {
    aFilter: [],
    aSearch: []
};

Metadata:

<EntityType Name="GroupNode" sap:content-version="1">
      <Key>
         <PropertyRef Name="Grpid"/>
      </Key>
      <Property Name="Grpid" Type="Edm.String" Nullable="false" MaxLength="8" sap:unicode="false" sap:label="Id Trainingsgruppe" sap:creatable="false" sap:updatable="false" sap:filterable="false"/>
      <Property Name="Short" Type="Edm.String" MaxLength="12" sap:unicode="false" sap:label="Kürzel Trainingsgruppe" sap:creatable="false" sap:updatable="false" sap:sortable="false" sap:filterable="false"/>
      <Property Name="Stext" Type="Edm.String" MaxLength="40" sap:unicode="false" sap:label="Bezeichnung Trainingsgruppe" sap:creatable="false" sap:updatable="false" sap:filterable="false"/>
      <Property Name="Begda" Type="Edm.DateTime" Precision="0" sap:unicode="false" sap:label="Beginndatum" sap:creatable="false" sap:updatable="false" sap:sortable="false" sap:filterable="false"/>
      <Property Name="Endda" Type="Edm.DateTime" Precision="0" sap:unicode="false" sap:label="Endedatum" sap:creatable="false" sap:updatable="false" sap:sortable="false" sap:filterable="false"/>
      <Property Name="Level" Type="Edm.Int32" sap:unicode="false" sap:label="Level" sap:creatable="false" sap:updatable="false" sap:sortable="false" sap:filterable="false"/>
      <Property Name="Parentid" Type="Edm.String" Nullable="false" MaxLength="8" sap:unicode="false" sap:label="ParentId" sap:creatable="false" sap:updatable="false" sap:filterable="false"/>
      <NavigationProperty Name="ChildGroupNodes" Relationship="Z_HR_LSO_WORKCENTER_SRV.GroupNodeToParent" FromRole="FromRole_GroupNodeToParent" ToRole="ToRole_GroupNodeToParent"/>
      <NavigationProperty Name="GroupToTrainingType" Relationship="Z_HR_LSO_WORKCENTER_SRV.GroupToTrainingType" FromRole="FromRole_GroupToTrainingType" ToRole="ToRole_GroupToTrainingType"/>
</EntityType>

We're working with OData V2, so there's no possibility to implement an FilterContains.All filter.

Is it even possible to filter through the child nodes of a sap.m.Tree in the front-end?

Upvotes: 5

Views: 5855

Answers (3)

Boghyon Hoffmann
Boghyon Hoffmann

Reputation: 18054

First of all, building the tree hierarchy via navigation is deprecated since 1.44. Instead, SAP recommends to leverage metadata annotations:

The use of navigation properties to build up the hierarchy structure is deprecated and it is recommended to use the hierarchy annotations [...].

Once the migration to the annotation approach is done, filter either client-side or server-side.

Client-side

Server-side

  • For server-side filtering, only the FilterType "Application" and the operationMode: "Server" are supported. In that case, the server needs to respond to the $filter request with a ready-made tree structure. The same applies to paging requests for sibling and child nodes.

Limitations

  • suspended: true in the binding info is not yet supported (Issue #3161).
  • The above content applies to OData V2 only.
    V4 ODataModel doesn't support tree binding at all yet (Issue #2728).

Upvotes: 4

Sagar Mohan
Sagar Mohan

Reputation: 145

Not sure if you managed to solve your problem, but I actually did this exact thing a few months ago.

In your controller, put the following function:

    onSearch: function(oEvent) {

        var searchVal = oEvent.getParameter("newValue");
        var treeFilter = new sap.ui.model.Filter("Text", sap.ui.model.FilterOperator.Contains, searchVal);
        var oBinding = this.getView().byId("tree").mBindingInfos.rows.binding;

        oBinding.filter(treeFilter, FilterType.Application);
        oBinding.expandToLevel(3);
    },

And this should work straight away. Whatever you enter in the search box, it will populate the tree (up to 3 levels, but this can be changed) with your filter.

The onSearch function is of course executed on the livechange of the search field.

Upvotes: 0

Ashley
Ashley

Reputation: 432

I think this may have something to do with the way you're binding the items, because I am able to filter on child nodes just fine in my example using your JS.

Check that this._oList.getItems() has all the items in it before you do the filter.

I'll post my code, so you can rebuild my project and get a feel for how it works. Let me know if you have more questions.




Page.view.xml

  <mvc:View
    controllerName="sap.m.sample.Tree.Page"
    xmlns:mvc="sap.ui.core.mvc"
    xmlns="sap.m">
    <SearchField value="{json>/search}" search="onSearch"/>
        <Tree
            id="Tree"
            items="{path: '/'}">
            <StandardTreeItem title="{text}"/>
        </Tree>
</mvc:View>

Page.controller.js

sap.ui.define(['sap/ui/core/mvc/Controller', 'sap/ui/model/json/JSONModel', 'sap/ui/model/Filter', 'sap/ui/model/FilterOperator'],
function(Controller, JSONModel, Filter, FilterOperator) {
    "use strict";

    var PageController = Controller.extend("sap.m.sample.Tree.Page", {
        onInit: function(evt) {
            // set explored app's demo model on this sample
            var oModel = new JSONModel(jQuery.sap.getModulePath("sap.m.sample.Tree", "/Tree.json"));
            this.getView().setModel(oModel);

            var oJSONModel = new JSONModel();
            this.getView().setModel("json", oJSONModel);

            this._oList = this.byId("Tree");
            this._oListFilterState = {
                aFilter: [],
                aSearch: []
            };
        },

        onSearch: function(oEvent) {
            if (oEvent.getParameters().refreshButtonPressed) {
                this.onRefresh();
                return;
            }

            var sQuery = oEvent.getParameter("query");
            if (sQuery) {
                this._oListFilterState.aSearch = [new Filter("text", FilterOperator.Contains, sQuery)];
            } else {
                this._oListFilterState.aSearch = [];
            }
            this._applyFilterSearch();
        },

        _applyFilterSearch: function() {
            var aFilters = this._oListFilterState.aSearch.concat(this._oListFilterState.aFilter);
                //oViewModel = this.getModel();

            this._oList.getBinding("items").filter(aFilters, "Application");

            // if (aFilters.length !== 0) {
            //  oViewModel.setProperty("/noDataText", this.getResourceBundle().getText("masterListNoDataWithFilterOrSearchText"));
            // } else if (this._oListFilterState.aSearch.length > 0) {
            //  oViewModel.setProperty("/noDataText", this.getResourceBundle().getText("masterListNoDataText"));
            // }
        }

    });

    return PageController;

});

Tree.json

[
{
    "text": "Node1",
    "ref": "sap-icon://attachment-audio",
    "nodes":
    [
        {
            "text": "Node1-1",
            "ref": "sap-icon://attachment-e-pub",
            "nodes":[
                {
                    "text": "Node1-1-1",
                    "ref": "sap-icon://attachment-html"
                },
                {
                    "text": "Node1-1-2",
                    "ref": "sap-icon://attachment-photo",
                    "nodes":[
                        {
                            "text": "Node1-1-2-1",
                            "ref": "sap-icon://attachment-text-file",
                            "nodes":[
                                {
                                    "text": "Node1-1-2-1-1",
                                    "ref": "sap-icon://attachment-video"
                                },
                                {
                                    "text": "Node1-1-2-1-2",
                                    "ref": "sap-icon://attachment-zip-file"
                                },
                                {
                                    "text": "Node1-1-2-1-3",
                                    "ref": "sap-icon://course-program"
                                }
                            ]
                        }
                    ]
                }
            ]
        },
        {
            "text": "Node1-2",
            "ref": "sap-icon://create"
        }
    ]
},
{
    "text": "Node2",
    "ref": "sap-icon://customer-financial-fact-sheet"
}

]

That's all you should need, but just incase you need these as well... index.html

<!DOCTYPE HTML>

<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta charset="utf-8">

    <title>Tree - Basic</title>

    <script id="sap-ui-bootstrap"
        src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
        data-sap-ui-libs="sap.m"
        data-sap-ui-theme="sap_belize"
        data-sap-ui-xx-bindingSyntax="complex"
        data-sap-ui-preload="async"
        data-sap-ui-compatVersion="edge"
        data-sap-ui-resourceroots='{"sap.m.sample.Tree": "./", "sap.ui.demo.mock": "mockdata"}'>
    </script>

    <!-- Application launch configuration -->
    <script>

        sap.ui.getCore().attachInit(function() {
            new sap.m.App ({
                pages: [
                    new sap.m.Page({
                        title: "Tree - Basic",
                        enableScrolling : true,
                        content: [ new sap.ui.core.ComponentContainer({
                            name : "sap.m.sample.Tree"
                        })]
                    })
                ]
            }).placeAt("content");
        });

    </script>
</head>

<!-- UI Content -->
<body class="sapUiBody" id="content" role="application">
</body>

Component.js

sap.ui.define(['sap/ui/core/UIComponent'],
function(UIComponent) {
"use strict";

var Component = UIComponent.extend("sap.m.sample.Tree.Component", {

    metadata : {
        rootView : "sap.m.sample.Tree.Page",
        dependencies : {
            libs : [
                "sap.m",
                "sap.ui.layout"
            ]
        },
        config : {
            sample : {
                files : [
                    "Page.view.xml",
                    "Page.controller.js",
                    "Tree.json"
                ]
            }
        }
    }
});

return Component;

});

Upvotes: 0

Related Questions