Show TOC

Step 15: Aggregation Binding Using a Factory FunctionLocate this document in the navigation structure

Instead of hard-coding a single template control, we will use a factory function to generate different controls based on the data received at runtime. This approach is much more flexible and allows complex or heterogeneous data to be displayed.

Preview
Figure 1: Controls generated based on data
Coding

You can view and download all files in the Explored app in the Demo Kit under Data Binding - Step 15.

webapp/view/App.view.xml
... 
	<Panel headerText="{i18n>panel3HeaderText}" class="sapUiResponsiveMargin" width="auto">
		<content>
			<List id="ProductList" headerText="{i18n>productListTitle}" items="{
				path: 'products>/Products',
				factory: '.productListFactory'
			}" />
		</content>
	</Panel>
...

The List XML element that previously held the product list is now reduced simply to a named, but otherwise empty placeholder. Without a factory function to populate it, this List would always remain empty.

webapp/controller/App.controller.js
sap.ui.define([ "sap/ui/core/mvc/Controller", "sap/ui/model/type/Currency" ],
function(Controller, Currency) {
	"use strict";
 
	return Controller.extend("sap.ui.demo.db.controller.App", {
		formatMapUrl : function(sStreet, sZip, sCity, sCountry) {
			return "https://maps.googleapis.com/maps/api/staticmap?zoom=13&size=500x300&markers="
					+ jQuery.sap.encodeURL(sStreet + ", " + sZip + " " + sCity + ", " + sCountry);
		},
		formatStockValue : function(fUnitPrice,
				iStockLevel, sCurrCode) {
			var sBrowserLocale = sap.ui.getCore().getConfiguration().getLanguage();
			var oLocale = new sap.ui.core.Locale(sBrowserLocale);
			var oLocaleData = new sap.ui.core.LocaleData(oLocale);
			var oCurrency = new Currency(oLocaleData.mData.currencyFormat);
			return oCurrency.formatValue([fUnitPrice * iStockLevel, sCurrCode ], "string");
		},
		onItemSelected : function(oEvent) {
			var oSelectedItem = oEvent.getSource();
			var oContext = oSelectedItem.getBindingContext("products");
			var sPath = oContext.getPath();
			var oProductDetailPanel = this.getView().byId("productDetailsPanel");
			oProductDetailPanel.bindElement({path : sPath, model : "products"});
		},
		productListFactory : function(sId,oContext) {
			var oUIControl = null;
 
			// Define the item description
			var sDescription = oContext.getProperty("ProductName") + " (" + oContext.getProperty("QuantityPerUnit") + ")";
 
			// This item is out of stock and discontinued
			// *and* discontinued?
			if (oContext.getProperty("UnitsInStock") === 0 && oContext.getProperty("Discontinued")) {
				// Yup, so use a
				// StandardListItem
				oUIControl = new sap.m.StandardListItem(sId, {
					icon : "sap-icon://warning",
					title : sDescription,
					info : { path: "i18n>Discontinued" },
					infoState : "Error"
				});
			} else {
				// Nope, so we will create an
				// ObjectListItem
				
				oUIControl = new sap.m.ObjectListItem(sId, {
					title : sDescription,
					number : {
						parts : [ "products>UnitPrice", "/currencyCode" ],
						type : "sap.ui.model.type.Currency",
						formatOptions : {
							showMeasure : false
						}
					},
					numberUnit : {
						path : "/currencyCode"
					}
				});
				
				// Is this item out of stock?
				if (oContext.getProperty("UnitsInStock") < 1) {
					// Nope, so this item is just temporarily out of stock
					oUIControl.addAttribute(new sap.m.ObjectAttribute({
						text : { path: "i18n>outOfStock" }
					}));
				}
			}
 
			// Set item active (so it is clickable) and attach the press event
			// handler for showing the details
			oUIControl.setType(sap.m.ListType.Active);
			oUIControl.attachPress(this.onItemSelected, this);
			return oUIControl;
		} 
	});
});

In the App controller, we create a new function called productListFactory. The types of controls returned from this factory function must be limited to those suitable for inclusion in the items aggregation of a sap.m.List object. In this case, we will return either a StandardListItem or an ObjectListItem using the following logic:

We decide which type of control to return by checking the current stock level and whether or not the product has been discontinued. This creates the following possible responses:
  1. First, eliminate the minority case.

    If the stock level is zero and the product has also been discontinued, then use a StandardListItem with a warning icon and a Product Discontinued message in the status property. All other possibilities will use an ObjectListItem.

  2. Create an ObjectListItem then for the remaining three possibilities.

    The product is in stock and the product has not been discontinued. This is the majority case.

  3. The stock level is zero, therefore we are temporarily out of stock. In this case, add a single ObjectAttribute that displays the Out of Stock message.

webapp/i18n/i18n.properties
...
# Product Details
...
outOfStock=Out of Stock
webapp/i18n/i18n_de.properties
...
# Product Details
...
outOfStock=Nicht Vorrätig

We add the missing texts to the properties files.

That's all - you completed the Data Binding tutorial!