Search code examples
jsondata-bindinglistitemsapui5

UI5: Dynamically build ListItems from JSON with different Icons


I have this simple XML View:

<core:View xmlns:core="sap.ui.core" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m"
        controllerName="listicons.list" xmlns:html="http://www.w3.org/1999/xhtml">
    <Page title="Title">
        <content>
            <List id="test-list"></List>
        </content>
    </Page>
</core:View>

In my controller, I call a method to build list items onInit. First of all set some data:

var data = {
        "products": [
              {
                  "prodName": "Apple",
                  "prodCountry": "Netherlands",
                  "price": "normal"
              },
              {
                  "prodName": "Orange",
                  "prodCountry": "Spain",
                  "price": "extra"
              },
              {
                  "prodName": "Strawberry",
                  "prodCountry": "Poland",
                  "price": "normal"
              }
          ]
        };
// create a Model with this data and attach it to the view
            var model = new sap.ui.model.json.JSONModel();
            model.setData(data);
            this.getView().setModel(model);
            var list = this.getView().byId("test-list");

Then I build the list and bind the items to it:

// bind the List items to the data collection
            list.bindItems({
                path : "/products", 
                sorter : new sap.ui.model.Sorter("prodName"),
                //template : listTmpl
                template : new sap.m.StandardListItem({
                    title: "{prodName}",
                    description: "{prodCountry}"
                })
            }); 

After I built the list and is alread rendered, I look after which items have an extra price and set an icon for them:

jQuery.each(list.getItems(), function(i, obj) {
                if(obj.mProperties.price == "extra") {
                    obj.setIcon("sap-icon://flag"); 
                }
            });

So. Everything works fine. But I am not happy with my solution, because I'd rather like to manipulate the data BEFORE rendering the list. I tried to build a list template directly before binding the items to the list and then use this template like:

var listTmpl = jQuery.each(data.products, function(i, a) {
            var lI = new sap.m.StandardListItem({
                title: "{prodName}",
                description: "{prodCountry}" 
            });
            if(a.price == "extra") {
                lI.setIcon("sap-icon://flag");
            }
            return lI;
        });

But then my list is not shown and I got an error in the console, saying

Missing template or factory function for aggregation items of Element sap.m.List ...

Does anyone have an idea how to improve my sol.? THX a lot..


Solution

  • IMHO, I think you can have the controller as clean as possible, and define most of the needed functionality (binding, template, sorter, and icon) in the XMLView:

    <List id="test-list" items="{
        path   : '/products', 
        sorter : [{
            path       : 'prodName', 
            descending : true
        }]
    }">
        <StandardListItem title="{prodName}" 
                          description="{prodCountry}" 
                          icon="{path:'price', formatter:'.getIconFlag'}" />
    </List>
    

    You then can rid of all the template binding and manipulation stuff you have in your controller, and you only need to specify the formatter function getIconFlag:

    getIconFlag : function (sPrice) {
        return sPrice === "extra" ? "sap-icon://flag" : null;
    }
    

    See the following working example:

    sap.ui.controller("view1.initial", {
        onInit : function(oEvent) {
            var oModel = new sap.ui.model.json.JSONModel();
            oModel.setData({
                "products": [
                    {
                        "prodName": "Apple",
                        "prodCountry": "Netherlands",
                        "price": "normal"
                    },
                    {
                        "prodName": "Orange",
                        "prodCountry": "Spain",
                        "price": "extra"
                    },
                    {
                        "prodName": "Strawberry",
                        "prodCountry": "Poland",
                        "price": "normal"
                    }
                ]
            });
    
            this.getView().setModel(oModel);
    
        },
    
        getIconFlag : function (sPrice) {
            return sPrice === "extra" ? "sap-icon://flag" : null;
        }
    
    });
    
    sap.ui.xmlview("main", {
        viewContent: jQuery("#view1").html()
    })
    .placeAt("uiArea");
    <script id="sap-ui-bootstrap"
        src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
        data-sap-ui-theme="sap_bluecrystal"
        data-sap-ui-xx-bindingSyntax="complex"
        data-sap-ui-libs="sap.m"></script>
    
    <div id="uiArea"></div>
    
    <script id="view1" type="ui5/xmlview">
        <mvc:View 
          controllerName="view1.initial"
          xmlns="sap.m"
          xmlns:core="sap.ui.core"
          xmlns:mvc="sap.ui.core.mvc" >
            <List id="test-list" items="{
                path: '/products', 
                sorter: [{
                    path: 'prodName', 
                    descending: true
                }]
            }">
                <StandardListItem title="{prodName}" description="{prodCountry}" icon="{path:'price', formatter:'.getIconFlag'}" />
            </List>
        </mvc:View>
    </script>