3

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

ho.s
  • 751
  • 3
  • 17
  • 42

2 Answers2

4

bind the value for price to a formatter-function. so you can create the icons dynamically from the value

list.bindItems({
    path : "/products", 
    sorter : new sap.ui.model.Sorter("prodName"),
    template : new sap.m.StandardListItem({
        title: "{prodName}",
        description: "{prodCountry}",
        /* bind items to factory-function */
        icon: {
            path: "price",
            formatter: function(price) {
                if (price == "extra") {
                    return "sap-icon://flag"; 
                }
            }
        }
    })
}); 

ps: i did not test this, but it should work like this. if you receive errors just comment.

herrlock
  • 1,454
  • 1
  • 13
  • 16
  • Thank you for your JS approach. I got the follwing error: `Uncaught Error: Aggregation "icon" does not exist in Element sap.m.StandardListItem` - But there is a property *icon* (which does just take a uri as the doc says?). The factory fn is not entered (tested with a log).. – ho.s Feb 19 '15 at 14:32
  • 1
    sorry, my error. it must be a formatter, not a factory – herrlock Feb 19 '15 at 15:57
  • I wonder if it is possible to find out which StandardListItem is the callee of the formatter function in the controller. I'd need to know that, because I'd like to set the color of the Item to red via css if the prodCountry is Poland. (just an example). Sth like `jQuery(this).find(".sapUiIcon").css("color", "red")` ? – ho.s Mar 06 '15 at 13:35
  • inside the formatter you should be able to use `this` to access that item – herrlock Mar 06 '15 at 14:19
  • ok thx, but .. as I call the getIconFlag function in the controller to keep the JS in the view small ´this´ *is the view*. maybe I have to write it directly in the view then (?) – ho.s Mar 06 '15 at 16:18
4

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>
Qualiture
  • 4,900
  • 7
  • 27
  • 38
  • first of all thx, this seems much cleaner to me too :-) don't know much about the formatting possibilities in XML Views yet. But: I have a problem with this: As soon as I just write my code exactly as yours `items={path: '/products'}` the list in the browser is just displaying "Keine Daten" (no data). If I replace it with `items={'/products'}` it works. But then I cannot build a object with other settings. What do I do wrong that I can't use the *path* syntax? – ho.s Feb 19 '15 at 12:13
  • That is strange... `items="{path: '/products'}"` should work just perfect, are you sure you did not forget any quotes? See https://sapui5.hana.ondemand.com/sdk/#docs/guide/0c803921b1bf4b3a97a038fbd51ef542.html for more info on formatters in XMLViews – Qualiture Feb 19 '15 at 12:31
  • I have no idea.. I deleted the whole project and set it up fresh. No quotes missing, had 4 eyes on that. It is just not working within XML View with this syntax. In JS `path : "/products"` works fine. – ho.s Feb 19 '15 at 13:06
  • 1
    Ah, one thing I also forget 9 times out of 10: for it to work, you must have set `data-sap-ui-xx-bindingSyntax="complex"` in your bootstrapper. Guess you forgot this as well ;) – Qualiture Feb 19 '15 at 13:08
  • This is it. Wow ok, this is just as "elegant" as missing quotes. I did have problems with the path syntax in my first few Controls too so I just decided to not use it :-) – ho.s Feb 19 '15 at 13:14
  • Sorry one last comment: I'd like to get deeper into the docs so I can try better to get my own solutions, so: Where for example do I get to know that I can make use of "formatter" (as an attribute in your XML View) or a "factory" function (as mentioned below) of the property icon for StandardListItem? Because the doc [just says](https://sapui5.netweaver.ondemand.com/sdk/#docs/api/symbols/sap.m.StandardListItem.html#setIcon) the icon property can process an *uri* as a param. – ho.s Feb 19 '15 at 14:51
  • The API doc you referred to just shows the types, methods and classes. How you use them, is totally up to you ;-) The formatter, for example, just returns an URI too so from the API it is perfectly ok. To know what extra UI5 goodies can be used, such as formatters, complex binding, etc, I would recommend to read the [Developer Guide](https://sapui5.hana.ondemand.com/sdk/docs/guide/2bfac839aa21483f89403dc4ab090724.html) section of the SDK; it describes all the extra UI5 functionality which can't be covered in the API docs – Qualiture Feb 19 '15 at 15:34
  • 1
    just for the record (anyone else who might earch what I was looking for): the [documentation for property binding](https://openui5.hana.ondemand.com/docs/guide/91f0652b6f4d1014b6dd926db0e91070.html) explains the use of formatter functions well (in addition to @Qualiture s link. So it is about property binding syntax and not icon specific as I thought.. – ho.s Feb 20 '15 at 08:57