UI5: Dynamically build ListItems from JSON with different Icons

data-binding, json, listitem, sapui5

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>

Problem

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..

Original source