Skip to Content

Hi!

I’ve been working with sap.ui.table.Table to adding some sorters and filters. The main purpose of this blog is overwrite default sorting by adding a custom menu to table column header. This code fragment will create a column with default sorting:


//Define the columns and the control templates to be used    
oTable.addColumn(new sap.ui.table.Column({    
    label: new sap.ui.commons.Label({text: "years"}),    
    template: new sap.ui.commons.TextView().bindProperty("text", "years"),    
    sortProperty: "years",    
    filterProperty: "years",    
    width: "200px"
}));

SAPUI5 table sort column algorithm depends on model type. number = number sorting, text = text sorting, etc.

Sometimes we use datasources (external or public services, etc) which contain bad typed attributes like numeric values typed as string. It is possible override default table column header menu to add a custom sorter.

This is an example of wrong sorting:

screenshot1.PNG

Step 1. Create function addColumnSorterAndFilter


This function will add a custom menu with sorting asc, desc and filtering functionality.


/**
 * Adds a custom sort menu for a given table
 *
 * @param oColumn Target table column to add custom menu
 * @param comparator Function to compare two values of column oColumn
 */
function addColumnSorterAndFilter(oColumn, comparator) {
  var oTable = oColumn.getParent();
  var oCustomMenu = new sap.ui.commons.Menu();
   
    oCustomMenu.addItem(new sap.ui.commons.MenuItem({
                text: 'Sort ascending',
                icon:"/com.sap.scn.demo/resources/sap/ui/table/themes/sap_goldreflection/img/ico12_sort_asc.gif",
                select:function() {
                 var oSorter = new sap.ui.model.Sorter(oColumn.getSortProperty(), false);
                 oSorter.fnCompare=comparator;
                 oTable.getBinding("rows").sort(oSorter);
                
                 for (var i=0;i<oTable.getColumns().length; i++) oTable.getColumns()[i].setSorted(false);                
                 oColumn.setSorted(true);
                 oColumn.setSortOrder(sap.ui.table.SortOrder.Ascending);
                }
    }));
    oCustomMenu.addItem(new sap.ui.commons.MenuItem({
     text: 'Sort descending',
        icon:"/com.sap.scn.demo/resources/sap/ui/table/themes/sap_goldreflection/img/ico12_sort_desc.gif",
        select:function(oControlEvent) {
             var oSorter = new sap.ui.model.Sorter(oColumn.getSortProperty(), true);
             oSorter.fnCompare=comparator;
             oTable.getBinding("rows").sort(oSorter);
                
             for (var i=0;i<oTable.getColumns().length; i++) oTable.getColumns()[i].setSorted(false);
            
             oColumn.setSorted(true);
             oColumn.setSortOrder(sap.ui.table.SortOrder.Descending);
        }
    }));
   
    oCustomMenu.addItem(new sap.ui.commons.MenuTextFieldItem({
  text: 'Filter',
  icon: '/com.sap.scn.demo/resources/sap/ui/table/themes/sap_goldreflection/img/ico12_filter.gif',
  select: function(oControlEvent) {
      var filterValue = oControlEvent.getParameters().item.getValue();
      var filterProperty = oControlEvent.getSource().getParent().getParent().mProperties.sortProperty;
      var filters = [];
      if (filterValue.trim() != '') {
      var oFilter1 = new sap.ui.model.Filter(filterProperty, sap.ui.model.FilterOperator.EQ, filterValue);
      filters = [oFilter1];   
      }
      oTable.getBinding("rows").filter(filters, sap.ui.model.FilterType.Application);
  }
    }));
   
    oColumn.setMenu(oCustomMenu);
    return oColumn;
}

Step 2. Create your custom comparator



/**
 * Integer comparator
 */
function compareIntegers(value1, value2) {
  if ((value1 == null || value1 == undefined || value1 == '') &&
  (value2 == null || value2 == undefined || value2 == '')) return 0;
  if ((value1 == null || value1 == undefined || value1 == '')) return -1;
  if ((value2 == null || value2 == undefined || value2 == '')) return 1;
  if(parseInt(value1) < parseInt(value2)) return -1;
  if(parseInt(value1) == parseInt(value2)) return 0;
  if(parseInt(value1) > parseInt(value2)) return 1;           
};


Step 3. Apply new menu to column


var oColumn = new sap.ui.table.Column({
  label: new sap.ui.commons.Label({text: "years"}),
  template: new sap.ui.commons.TextView().bindProperty("text", "years"),
  sortProperty: "years",
  filterProperty: "years"
  });
  oTable.addColumn(oColumn);
addColumnSorterAndFilter(oColumn, compareIntegers);

Result


screenshot2.PNG


Any suggestions are welcome!

Kind regards


To report this post you need to login first.

12 Comments

You must be Logged on to comment or reply to a post.

  1. Kai Lucas

    Hi Angel,

    thank you for your solution, i’m new to sapui5 and it helps understanding sorters and filters. With my odata model the sorter causes a modified odata request to the gateway and uses the $filter oder orderby options of the gateway. In my case the gateway service hasn’t implemented filter or order functionalities, so the order in the table won’t change. Of course i tried the gateway service with the rest client, too.

    I have all required odata already in the table. So my question is: is there a way to sort and filter the odatamodel localy in the browser. I can imagine that it’s possible to modify the odata model with javascipt.

    Thank you

    Kai

    (0) 
    1. Angel Puertas Post author

      Hi Kai,

      I think there’s an option. You could avoid this creating a JSONModel after your read odata call.

      For example:

      yourModel.read(

        yourModelUrlQuery,

        ”,

        {},

        true,

        function (data, response)

        {  

          if (data != undefined) {

            if(data.results.length > 0){

              var modelJSON = new sap.ui.model.json.JSONModel();

              modelJSON.setData({

                    businessData:data.results

               });

              //bind this JSON Model to your table

           }

         }

      );

      If you bind JSON Model to your table you’ll enable client sorting & filtering.

      Kind regards

      (0) 
      1. Kai Lucas

        Hi Angel,

        thank you very much, with an json model  this works very well on client site.

        Furthermore i put all models in a model.js file, that’s included in the index.html to make the models available everywhere.

        model.js:

        //global variables

        var allStockItemModel = new sap.ui.model.json.JSONModel();

        var singleStockItemModel  = new sap.ui.model.json.JSONModel();

        var oModel;

        //var sServiceUrl = “http:HOST:PORT/GW-SERVICE URL”;  //for productive

        var sServiceUrl = “proxy/GW-SERVICE-URL”; //for local test

        var user = “user”;

        var pw = “pw”;

        gw_param = “a parameter”

        jQuery.sap.require(“sap.ui.commons.MessageBox”);            //do not delete – is needed for correct functionality of messagebox

        //methods

        function initModel(){      

            oModel = new sap.ui.model.odata.ODataModel(sServiceUrl, false, user, pw);

            sap.ui.getCore().setModel(oModel);

        }

          

        function getAllStockItems(){

          

            var param = [“$filter=param1 eq ‘”+gw_param+”‘”];

             oModel.read(“/ENTITYNAME”, null, param, true,

                        function (oData, response){

                       allStockItemModel.setData(oData);                   

                            //document.write(allStockItemModel.getJSON()); //for test

                        },

                        function (oError){

                            alert(“Error while loading allStockItemModel”);

                        });

        }

        Thank you very much and best wishes

        Kai

        (0) 
  2. Miguel Mateos

    Great post!!!

    Only a little bug 🙂   For MenuTextFieldItem you must use ‘label’ property because ‘text’ is undefined.

    Thanks and best regards

    (0) 
    1. Kai Lucas

      Hi Follower,

      i guess there’s no way to use the filter without an binding. If you filter the model, you would probaply lose your data, i don’t know if that make scence.

      Furthermore you should add an filter to the odata request (if you use one) and get only the needed data.

      For manual actions on the model you could iterate through the json data manually and sort or filter the model.

      //Here’s an example model:

      var oModel = new sap.ui.model.json.JSONModel();

      oModel.setData({results:[{key:1},{key:2},{key:3},{key:4},{key:5}]});

      //iterate through and do what you would, perhaps copy matching values to another model

      $.each(oModel.getProperty(“/results”),function( index, entry){

              if(entry.key > 3){

                  alert(“greater 3 is ” + entry.key);

      }});

      For simple filtering an array you also can use somthing like:

      var aResults= oModel.getProperty(“/results”);

      aResults.filter(function (x) {
       
      return x.key > 3;
      });


      and put the result back to the model…


      also see jquery – Javascript: How to filter object array based on attributes? – Stack Overflow


      Best wishes


      Kai

      (0) 
  3. Andy Bostian

    Very helpful.  I was needing some help on custom sorting today, so thank you for this post.  I noticed also that this guy:

    1. function compareIntegers(value1, value2) { 
    2.   if ((value1 == null || value1 == undefined || value1 == ) && 
    3.   (value2 == null || value2 == undefined || value2 == )) return 0
    4.   if ((value1 == null || value1 == undefined || value1 == )) return1
    5.   if ((value2 == null || value2 == undefined || value2 == )) return 1
    6.   if(parseInt(value1) < parseInt(value2)) return1
    7.   if(parseInt(value1) == parseInt(value2)) return 0
    8.   if(parseInt(value1) > parseInt(value2)) return 1;             
    9. }; 

    Can be reduced using some nifty features of javascript, to this:

    1. function compareIntegers(a, b) { 
    2.   return parseInt(a || 0) – parseInt(b || 0);           
    3. }; 

    Hope this is helpful.

    Thanks,

    Andy

    (0) 
  4. Khabir Ahmad Raja

    Hi guys I am facing a problem with filter in sap.m.List. Initial filter is working fine but when a user changes the filter in run time the List doesn’t updated according to the new filter.

    Any idea how to change the List contents according to the change in filter request?

    var oSelection = 2;

    var oFilter = new sap.ui.model.Filter(“Quantity”, sap.ui.model.FilterOperator.EQ, oSelection);

    var oComboBox1 = new sap.m.ComboBox(“Combobox”,{

      selectionChange: function(oControlEvent) {

      oSelection =  3;   //$(“#Combobox-inner”).val();

      sap.ui.getCore().byId(“list”).getModel().refresh(true);

      }});

    Regards,

    Khabir

    (0) 
    1. Kai Lucas

      Hi Kahir,

      i would suggest to update your binding like:

           oComboBox1.bindAggregation(“items”,{

                path: “yout path”,

                startIndex: 1,

                template: …. or factory: ….,

                sorter:[

                               new sap.ui.modelSorter(sKey,bSortDesc),

                          ]

           }

      Best wishes

      Kai

      (0) 
  5. Yusufi jiruwala

    Hi,

     

    Thanks for solution,  I just need to know how we can do this same customization in grouping table.

     

    I cant find any resources. Thanx in advance.

     

    (0) 

Leave a Reply