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
... 
            <attributes>
              <ObjectAttribute text="{ path: 'Status', formatter: '.getStatusText' }"/>
            </attributes>
          </ObjectListItem>
        </items>
      </List>
    </content>
  </Panel>
  <Panel headerText="{i18n>panel4HeaderText}" class="sapUiResponsiveMargin" width="auto">
    <content>
      <List id="ProductList" headerText="{i18n>productListTitle}" />

    </content>
  </Panel>
</mvc:View>

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
...
    productListFactory: function(sId, oContext) {
      var oUIControl = null;
      // Define the item description
      var sDescription  = oContext.getProperty("ProductName") +
                          " (" + oContext.getProperty("QuantityPerUnit") + ")";
      // Is this item is both out of stock *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: [{path: "products>UnitPrice"},
                                 {path: "/currencyCode"}],
                         type: "sap.ui.model.type.Currency",
                         formatOptions: {showMeasure: false}
                       },
          numberUnit : { path: "/currencyCode"}
        });
        // Is this item in stock?
        if (oContext.getProperty("UnitsInStock") > 0) {
          // First object attribute displays the current stock level
          oUIControl.addAttribute(new sap.m.ObjectAttribute({
            title : { path: "i18n>UnitsInStock" },
            text  : { path: "products>UnitsInStock" }
          }));
          // Second object attribute displays the current stock value
          oUIControl.addAttribute(new sap.m.ObjectAttribute({
            title : { path: "i18n>stockValue" },
                      text  : { parts: [{path: "products>UnitPrice"},
                                        {path: "products>UnitsInStock"},
                                        {path: "/currencyCode"}],
                                formatter: this.formatStockValue }
          }));
          // Has this product been discontinued?
          if (oContext.getProperty("Discontinued")) {
            // Yup, so we're selling off the last remaining stock items
            // Set the status of the first attribute to "discontinued"
            oUIControl.setFirstStatus(new sap.m.ObjectStatus({
              text: { path: "i18n>Discontinued" },
                      state: "Error"
            }));
          }
        }
        else {
          // 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;
    },

    onInit: function() {
      // Get a reference to the Product List using the XML element's id
      var oProductList = this.getView().byId("ProductList");
      // In addition to binding the "items" aggregation in the list to the Products array
      // in the "products" model, the productListFactory function is also passed as a parameter
      oProductList.bindAggregation("items", "products>/Products", this.productListFactory.bind(this));

    }
...

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 four 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, so add two ObjectAttributes to the ObjectListItem to display the current stock level and value. This is the majority case.

  3. The product is in stock but has also been discontinued, so we are simply selling off the last remaining stock items. Therefore, in addition to the two ObjectAttributes added above, use the status property of the first attribute to indicate that this product has been discontinued.

  4. The product has not been discontinued but 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.