For easily detecting and managing product shortages in our app, we will add a quick filter for the worklist table. Users can press the filter tabs to display the products according to whether they are in stock, have low stock or no stock. The table will update accordingly and show only the products matching the criteria.
You can view and download all files in the Explored app in the Demo Kit at Worklist App - Step 4.
webapp/view/Worklist.view.xml
#!xml… <Table …> <!--<headerToolbar> <Toolbar> <Title id="tableHeader" text="{worklistView>/worklistTableTitle}"/> <ToolbarSpacer /> <SearchField id="searchField" tooltip="{i18n>worklistSearchTooltip}" search="onSearch" width="auto"/> </Toolbar> </headerToolbar>--> <columns> …
As a preparation step, comment out the header toolbar of the table in the view.
webapp/view/Worklist.view.xml
#!xml... <mvc:View controllerName="myCompany.myApp.controller.Worklist" xmlns:mvc="sap.ui.core.mvc" xmlns:semantic="sap.m.semantic" xmlns="sap.m"> <semantic:FullscreenPage id="page" navButtonPress="onNavBack" showNavButton="true" title="{i18n>worklistViewTitle}"> <semantic:content> <IconTabBar id="iconTabBar" select="onQuickFilter" expandable="false" applyContentPadding="false"> <items> <IconTabFilter key="all" showAll="true" count="{worklistView>/countAll}" text="{i18n>WorklistFilterProductsAll}"/> <IconTabSeparator/> <IconTabFilter key="inStock" icon="sap-icon://message-success" iconColor="Positive" count="{worklistView>/inStock}" text="{i18n>WorklistFilterInStock}"/> <IconTabFilter key="shortage" icon="sap-icon://message-warning" iconColor="Critical" count="{worklistView>/shortage}" text="{i18n>WorklistFilterShortage}"/> <IconTabFilter key="outOfStock" icon="sap-icon://message-error" iconColor="Negative" count="{worklistView>/outOfStock}" text="{i18n>WorklistFilterOutOfStock}"/> </items> </IconTabBar> <Table id="table" busyIndicatorDelay="{worklistView>/tableBusyDelay}" class="sapUiResponsiveMargin sapUiNoMarginTop" growing="true" growingScrollToLoad="true" noDataText="{worklistView>/tableNoDataText}" updateFinished="onUpdateFinished" width="auto" items="{ path: '/Products', sorter: { path: 'ProductName', descending: false }, parameters: { 'expand': 'Supplier' } }"> ...
Total Stock
This tab will simply show the overall number of products that has been returned by the data service. The count property is bound to a local view model and the number will be updated in the controller later in this step. This tab will show a larger number only (optional) and no icon by using the showAll property.
Out of Stock
This tab will show all the products that are out of stock. We choose a matching icon from the icon font and set the icon color to the semantic Negative state so that it will appear in red.
Shortage
This tab will show products that have less than 10 pieces remaining with a semantic Critical state that will make the icon appear in orange. The count of the number of low stock products will be displayed on the tab and the icon will appear in orange.
Plenty in Stock
Each IconTabFilter element has a key property that is used to identify the tab that was pressed in the event handler onQuickFilter that is registered on the IconTabBar control directly. The event handler implementation does the actual filtering on the table and is defined in the controller.
webapp/controller/Worklist.controller.js
#!js... onInit: function() { var oViewModel, iOriginalBusyDelay, oTable = this.byId("table"); // Put down worklist table's original value for busy indicator delay, // so it can be restored later on. Busy handling on the table is // taken care of by the table itself. iOriginalBusyDelay = oTable.getBusyIndicatorDelay(); this._oTable = oTable; // keeps the search state this._oTableSearchState = []; // Model used to manipulate control states oViewModel = new JSONModel({ worklistTableTitle: this.getResourceBundle().getText("worklistTableTitle"), saveAsTileTitle: this.getResourceBundle().getText("worklistViewTitle"), shareOnJamTitle: this.getResourceBundle().getText("worklistViewTitle"), shareSendEmailSubject: this.getResourceBundle().getText("shareSendEmailWorklistSubject"), shareSendEmailMessage: this.getResourceBundle().getText("shareSendEmailWorklistMessage", [location.href]), tableNoDataText: this.getResourceBundle().getText("tableNoDataText"), tableBusyDelay: 0, inStock: 0, shortage: 0, outOfStock: 0, countAll: 0 }); this.setModel(oViewModel, "worklistView"); // Create an object of filters this._mFilters = { "inStock": [new sap.ui.model.Filter("UnitsInStock", "GT", 10)], "outOfStock": [new sap.ui.model.Filter("UnitsInStock", "LE", 0)], "shortage": [new sap.ui.model.Filter("UnitsInStock", "BT", 1, 10)], "all": [] }; // Make sure, busy indication is showing immediately so there is no // break after the busy indication for loading the view's meta data is // ended (see promise 'oWhenMetadataIsLoaded' in AppController) oTable.attachEventOnce("updateFinished", function() { // Restore original busy indicator delay for worklist's table oViewModel.setProperty("/tableBusyDelay", iOriginalBusyDelay); }); }, ...
As a preparation step for the filter tabs we add properties for the counters into the local view model of the worklist controller. We initialize the four values with 0 each. Furthermore, we create an object _mFilters that contains a filter for each tab. We will use the filters for filtering the table below the tabs. The properties in _mFilters correlate to the keys of the IconTabFilter controls we defined above in the Worklist.view.xml file. This way we can easily access a filter for a given tab based on the key of the corresponding tab.
Creating a simple filter requires a binding path as first parameter of the filter constructor (e.g. "UnitsInStock"), a filter operator (e.g. "GT") as second argument, and a value to compare (e.g. 10) as the third argument. We create such filters for all three tabs with different filter operators as described in the view part above. Additionally, we create an all filter which is an empty array for clearing the binding again (when the user chooses the All tab).
webapp/controller/Worklist.controller.js
#!js... onUpdateFinished: function(oEvent) { // update the worklist's object counter after the table update var sTitle, oTable = oEvent.getSource(), oViewModel = this.getModel("worklistView"), iTotalItems = oEvent.getParameter("total"); // only update the counter if the length is final and // the table is not empty if (iTotalItems && oTable.getBinding("items").isLengthFinal()) { sTitle = this.getResourceBundle().getText("worklistTableTitleCount", [iTotalItems]); // Get the count for all the products and set the value to 'countAll' property this.getModel().read("/Products/$count", { success: function (oData) { oViewModel.setProperty("/countAll", oData); } }); // read the count for the unitsInStock filter this.getModel().read("/Products/$count", { success: function (oData) { oViewModel.setProperty("/inStock", oData); }, filters: this._mFilters.inStock }); // read the count for the outOfStock filter this.getModel().read("/Products/$count", { success: function(oData){ oViewModel.setProperty("/outOfStock", oData); }, filters: this._mFilters.outOfStock }); // read the count for the shortage filter this.getModel().read("/Products/$count", { success: function(oData){ oViewModel.setProperty("/shortage", oData); }, filters: this._mFilters.shortage }); } else { sTitle = this.getResourceBundle().getText("worklistTableTitle"); } this.getModel("worklistView").setProperty("/worklistTableTitle", sTitle); }, ...
The v2.ODataModel will automatically bundle these read requests to one batch request to the server (if batch mode is enabled).
webapp/controller/Worklist.controller.js
#!js...
_applySearch: function(oTableSearchState) {
...
},
/**
* Event handler when a filter tab gets pressed
* @param {sap.ui.base.Event} oEvent the filter tab event
* @public
*/
onQuickFilter: function(oEvent) {
var oBinding = this._oTable.getBinding("items"),
sKey = oEvent.getParameter("selectedKey");
oBinding.filter(this._mFilters[sKey]);
}
...
Next, we implement the handler for the select event of the IconTabBar. In this event handler we get a reference to the binding for the items aggregation of our table and store it in the variable oBinding. Then we read the parameter selectedKey from the event object to find out which tab has been selected. This selectedKey is used to get the correct filter for the selected tab. Next, we simply call filter method on oBinding and pass the correct filter of the selected tab.
The filters are always applied as an array on the binding level, so you don't need to take care of managing the data, the data binding features of SAPUI5 will automatically take care.
webapp/i18n/i18n.properties
#!properties
...
#XTIT: The title of the products quick filter
WorklistFilterProductsAll=Products
#XTIT: The title of the out of stock products filter
WorklistFilterOutOfStock=Out of Stock
#XTIT: The title of the low stock products filter
WorklistFilterShortage=Shortage
#XTIT: The title of the products in stock filter
WorklistFilterInStock=Plenty in Stock
#~~~ Object View ~~~~~~~~~~~~~~~~~~~~~~~~~~
...
We finally add the texts for the tab filters to the resource bundle. Copy the text definitions from the code section above to the end of the Worklist View section in the i18n file.
Now run the app again and click on the filter icons on top of the table. The products should be filtered according to the selection in the filter bar and the count should match the number of items displayed.