Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 

This is the third part of a tutorial series about how to build your own SAP Fiori Approve Purchase Order app.


 


The purpose of this tutorial is to show you step-by-step how to build your own SAP Fiori Approve Purchase Orders app and provides additional insights into why certain aspects have been developed as they are.


Please see the introductory post (Tutorial: Build Your Own SAP Fiori Approve Purchase Order App) for background information regarding this tutorial.


Previously posted chapters can be find here:




In this chapter, we set up the app using the SAP Web IDE and ran the app using mock data.


In this third part, we're going to adapt the list screen of the app.


Adapt the list screen

We’d like to see more details in the entries on the left in the list, such as the number of items and a change date.

Each item currently looks like this:



 

 

 

and we’d like it to look more like this:



 

To make these changes, go to the View folder --> Master.view.xml file.

 

Double-click the file to edit it and search for the ObjectListItem section. The ObjectListItem definition should look like this:

 



File: view/Master.view.xml

 

<ObjectListItem

type="{= ${device>/system/phone} ? 'Active' : 'Inactive'}"

press="onSelectionChange"

title="{SupplierName}"

number="{

path: 'GrossAmount',

formatter: '.formatter.currencyValue'

}"

numberUnit="{CurrencyCode}">

</ObjectListItem>

 

We want to replace the code with the following code:


File: view/Master.view.xml

 

<items>

<ObjectListItem

                              type="{= ${device>/system/phone} ? 'Active' : 'Inactive'}"

                              press="onSelectionChange"

                              title="{SupplierName}"

                              number="{

                                              path: 'GrossAmount',

                                              formatter: '.formatter.currencyValue'

                              }"

                              numberUnit="{CurrencyCode}">

</ObjectListItem>


 

<ObjectListItem id="listItem"


                                intro="{parts:[{path: 'i18n>xfld.orderedBy'}, {path: 'OrderedByName'}], formatter: 'jQuery.sap.formatMessage'}"

                                number="{parts: [ {path: 'GrossAmount'}, {path: 'CurrencyCode'}], type : 'sap.ui.model.type.Currency', formatOptions: { showMeasure: false }}"

                                numberUnit="{CurrencyCode}" press="onSelect" title="{SupplierName}" type="{= ${device>/system/phone} ? 'Active' : 'Inactive'}">

                                <secondStatus>

                                              <ObjectStatus id="changedAtObjectStatus"
text="{path: 'ChangedAt', type: 'sap.ui.model.type.Date',


                                                              formatOptions: { style: 'medium',  strictParsing: true,relative: true, relativeScale:'auto'}}"/>

                                </secondStatus>

                                <attributes>

                                              <!--empty attribute added to align ItemCount with ChangeAt-->

                                              <ObjectAttribute id="emptyAttribute"/>

                                                <ObjectAttribute id="itemCountObjectAttribute" text="{path: 'ItemCount', formatter: '.formatter.items'}"/>

                                </attributes>

</ObjectListItem>


</items>

 



Do you want to reformat your code? You can use the built-in code beautifier. Click Edit->Beautify

 


 


Requirement for stable IDs

There are a number of features that are dependent on the stability of a given UI like automated test tools or UI Flexibility (end user personalization, UI configuration by key users and admins, UI extensibility). All these functions are based on specific instances controls and need to be able to reference these controls. This is done using the ID of the control. If after a software upgrade some of the controls disappear or the ID is changed, the functionality breaks. It is a good practice to define the ID as the very first attribute of the tag.

 

We want to display the change date relative to the current date. We could use a formatter, but UI5 also provides a handy formatting option we can use. First, we specify the type of the data we’re working with (sap.ui.model.type.Date). We want the date to be relative to the current date, for example, yesterday; we do this using relative:true. Additionally, we set the relativeScape to auto. That way, UI5 will select the relative scale depending on the difference between the given date and now.

 

The code contains two new formatters. One used for the intro property field and a second one for the number property.The intro property is a concatenation of a label text and the OrderedByName property.  For this we use the formatMessage function and use binding to pass the data into the function. The result is a string that is stored in the intro property. For the number property, we need to implement our own formatter that calculates the number of items.

 

Formatters are defined in the model/formatter.js file. To provide access to formatters, the BaseController already contains a formatter property.  All view controllers can access this property since they inherit from BaseController .To access a controller property from a view definition, simply add a “.” to the property. So, we have to edit the code in the formatter.js file. To do this, go to the model folder -->  formatter.js

 



File: model/formatter.js

  1. sap.ui.define([


"sap/ui/core/format/DateFormat"           

], function (DateFormat) {

"use strict";

 

return {

/**

* Rounds the currency value to 2 digits

*

* @public

* @Param {string} sValue value to be formatted

* @returns {string} formatted currency value with 2 digits

*/

currencyValue : function (sValue) {

if (!sValue) {

return "";

}

 

return parseFloat(sValue).toFixed(2);

},

                                             

                                              // formatter used in the master list to display how many items are available for each PO

                                              items: function(iItems) {

                                                              if (isNaN(iItems)) {

                                                                              return "";

                                                              }

                                                              var oBundle = this.getResourceBundle();

                                                              return (iItems === 1) ? oBundle.getText("xfld.item") : oBundle.getText("xfld.items", [iItems]);

                                              }

};

 

}

);

 

This definition contains three new translatable fields we need to define in i18n/i18n.properties.

 



File: i18n/i18.properties

#~~~ Master View ~~~~~~~~~~~~~~~~~~~~~~~~~~

 

#XFLD: Prefix to ordered-by attribute

xfld.orderedBy=Ordered by {0}

 

#XFLD: Number of items

xfld.items={0} Items

#XFLD: Exactly one item

xfld.item=1 Item

 

We use the prefix #XFLD because the text is used on fields on the UI. It is a good practice to indicate the usage of a text element with the used prefix.

You may have noticed that our window title contains the technical name of the entity set.

 

This is because the project wizard has not found a suitable human readable text in the metadata we provided. In fact, the generated i18n.properties files contains many occurences of this technical name. We can simply relace Z_C_PurchaseOrder with Purchase Order for all values in the generated i18n.properties file.

 


Text elements


























































Value Description
YMSG Message other than an instruction
YINS Instruction for a user
XTOL Explanatory text for an UI element, such as a tooltip
XFLD Label for a component other than buttons and titles; sample components: column heading
XBUT Button
XMIT Menu item, either top-level like "File" or lower-level like "Save as..."
XLST Item in an enumeration, such as a list or a drop-down list
XTIT Title or caption
XACT Text with explicit importance for accessibility (sounds like cross-classification since,
for example, and "alt" text for a HTML-related image may be an "explanation" with special relevance for accessibility)
XLNK Hyperlink
XGRP Group header or table section header
XSEL Value such as a status.  For example: "In Process", "Shipped" or "Open"

 

 

Next, let’s tweak the sorting of the list so that the latest items are displayed on top of the list. Go to view --> Master.view.xml and find the following code:

 



File: view/Master.view.xml

<List id="list"

items="{


              path: '/Z_C_Purchaseorder',


              sorter: {


                              path: 'SupplierName',


                              descending: false


                              },


              groupHeaderFactory: '.createGroupHeader'


}"


 


and replace the code with the following.



<List id="list"

items="{


path: '/Z_C_Purchaseorder',


sorter: [


{path: 'ChangedAt', descending: true},


{path: 'POId', descending: false}],


parameters:{select: 'POId,OrderedByName,SupplierName,GrossAmount,CurrencyCode,ChangedAt,ItemCount'}


            }"

 

You may notice that we also added the properties used in the list as parameters. This is not necessary but as a list may operate on a bigger data set on the backend, passing this information to the backend via the select property can be used to optimize the performance.

 

Adding this code interferes with generic sort/filter functionality of the bottom of the list.



 

Especially because even without the sorter, the data is not in a shape that justifies this functionality. So let’s just remove it by deleting the following code at the end of the file:



File: view/Master.view.xml

<semantic:sort>

                <semantic:SortSelect

                                              id="sort"

                                              selectedKey="{masterView>/sortBy}"

                                              change="onSort">

                <core:Item

                                              id="masterSort1"

                                              key="SupplierName"

                                              text="{i18n>masterSort1}"/>

                <core:Item

                                              id="masterSort2"

                                              key="GrossAmount"

                                              text="{i18n>masterSort2}"/>

</semantic:SortSelect>

</semantic:sort>

 

<semantic:filter>

                <semantic:FilterAction

                                              id="filter"

                                              press="onOpenViewSettings" />

</semantic:filter>

 

                <semantic:group>

                              <semantic:GroupSelect

                                              id="group"

                                              selectedKey="{masterView>/groupBy}"

                                              change="onGroup">

                              <core:Item

                                              id="masterGroupNone"

                                              key="None"

                                              text="{i18n>masterGroupNone}"/>

                                              <core:Item

                                                              id="masterGroup1"

                                                              key="GrossAmount"

                                                              text="{i18n>masterGroup1}"/>

                </semantic:GroupSelect>

</semantic:group>

 

There are a couple of translatable texts and event handlers used in this code. For the sake of simplicity, we won’t cleanup the controller code and i18n.properties file.

 

I hope the third part to the tutorial has sparked your interest in the chapters to come. Next time, we'll adapt the detail screen of the app.