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: 
0 Kudos
This is the fourth 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 chapter, we adapted the list screen of the app.

In this fourth part, we’re going to adapt the detail screen of the app.

Adapt the detail screen

We’ll now do some adjustments on the detail screen. Currently it looks like below. The IconTabBar has no value, and there are no different views to switch between. Additionally, the purchase order item table doesn’t contain enough information and neither does the header area.



Let’s change it to look something like this:



 

The detail screen is defined in the detail.view.xml file located in the view folder. We’ll use a vertical layout from sap.ui.layout and a form from sap.ui.layout.form. Both are not defined with corresponding namespaces so far, so let’s define them.


File: view/Detail.view.xml


<mvc:View

controllerName="acme.purchaseorder.controller.Detail"

xmlns="sap.m"

xmlns:mvc="sap.ui.core.mvc"

xmlns:semantic="sap.m.semantic"

xmlns:l="sap.ui.layout"

xmlns:form="sap.ui.layout.form"

xmlns:footerbar="sap.ushell.ui.footerbar">

Since we want to have more information in the header area, we need to think about the layout of the data. We’ll use a vertical layout from our newly introduced namespace and enhance the ObjectHeader definition.


File: view/Detail.view.xml

 

<semantic:content>

<ObjectHeader

id="objectHeader"

title="{SupplierName}"

number="{

path: 'GrossAmount',

formatter: '.formatter.currencyValue'

}"

numberUnit="{CurrencyCode}">

</ObjectHeader>

<l:VerticalLayout width="100%">

<ObjectHeader id="objectHeader"

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}" responsive="true" title="{SupplierName}">

<statuses>

<ObjectStatus id="status"

text="{path: 'ChangedAt', type: 'sap.ui.model.type.Date',

formatOptions: { style: 'medium', strictParsing: true, relative: true }}"

title="{/#Z_C_PurchaseorderType/ChangedAt/@sap:label}"/>

</statuses>

</ObjectHeader>

</l:VerticalLayout>

<IconTabBar

The amount needs to be formatted as currency. The default currency formatter displays the amount and the currency next to each other. But since the numberUnit property already displays the currency, we’ll suppress the currency being displayed by the formatter using showMeasure:false.

For the object status, we need to provide a title. The value is based on the property ChangedAt from the PurchaseOrder entity set. If you take a look at the definition from the metadata.xml we uploaded at the very beginning, you’ll recognize that there is already a label defined in the metadata via annotations in the OData model. We’ll simply link our label to this annotation by using the binding syntax to metadata, starting with a hash (#) and qualifying the EntityType and the annotation we want to use as label.  Smart Controls do this automatically, but since we have defined the ObjectStatus manually, we need to create this binding on our own.

Annotation binding is always the preferred choice, especially when dealing with translatable text. Only define text in i18n.properties in case the metadata does not provide the required text. If possible, add the information to the metadata. We’ll discuss in a later section how this metadata is generated based on the backend data model.


File: localService/metadata.xml


<EntityType Name="PurchaseOrder" sap:content-version="1" sap:is-thing-type="true">

<Key>

<PropertyRef Name="POId"/>

</Key>

<Property Name="ChangedAt" Precision="7" Type="Edm.DateTime" sap:creatable="false" sap:label="Changed At" sap:updatable="false"/>

The Object Header definition reuses text from i18n.properties and formatters that we introduced previously. Since we don’t need the IconTabBar, we'll simply remove it:


File: view/Detail.view.xml

<IconTabBar

id="iconTabBar"

class="sapUiResponsiveContentPadding">

<items>

<IconTabFilter

id="iconTabBarFilter1"

icon="sap-icon://hint"

tooltip="{i18n>detailIconTabBarInfo}">

</IconTabFilter>

<IconTabFilter

id="iconTabBarFilter2"

icon="sap-icon://attachment"

tooltip="{i18n>detailIconTabBarAttachments}">

</IconTabFilter>

</items>

</IconTabBar>

 

This also eliminates the need for two text elements that are part of i18n.properties.



File: i18n/i18n.properties

#XTOL: Icon Tab Bar Info

detailIconTabBarInfo=Info

#XTOL: Icon Tab Bar Attachments

detailIconTabBarAttachments=Attachments

 

We’ll now add a section called General Information between the header object and the table.  It’ll contain basic information about the selected purchase order. We’ll use a form from our newly introduced form namespace and add this to the vertical layout:



File: view/Detail.view.xml


</ObjectHeader>

<form:SimpleForm id="attributesSimpleForm" class="sapUiForceWidthAuto sapUiResponsiveMargin" columnsL="1" columnsM="1" emptySpanL="5" emptySpanM="5" labelSpanL="3" labelSpanM="3" layout="ResponsiveGridLayout" maxContainerCols="2" minWidth="1024" title="{i18n>xtit.formTitle}">

<Label id="poIdLabel" text="{/#Z_C_PurchaseorderType/POId/@sap:label}"/>

<Text id="poIdText" text="{POId}"/>

<Label id="deliveryDateLabel" text="{/#Z_C_PurchaseorderType/DeliveryDateEarliest/@sap:label}"/>

<Text id="deliveryDateText" text="{=${LaterDelivDateExist} > 1 ? ${parts:[{path: 'i18n>xfld.orLater'}, {path: 'DeliveryDateEarliest', type: 'sap.ui.model.type.Date', formatOptions: { style: 'medium'}}], formatter: 'jQuery.sap.formatMessage'} : ${path:'DeliveryDateEarliest', type: 'sap.ui.model.type.Date', formatOptions: { style: 'medium'}} }"/>

<Label id="deliveryAddressLabel" text="{/#Z_C_PurchaseorderType/DeliveryAddress/@sap:label}"/>

<Text id="deliveryAddressText" text="{DeliveryAddress}"/>

</form:SimpleForm>

</l:VerticalLayout>

 

Since there could be different delivery dates for the purchase order items, we don’t need to check the items because the model already contains a flag (LaterDelivDateExist) indicating different delivery dates. So, we'll use expression binding to determine the text to be printed in a declarative way. In case a later delivery date exists, we add the text “or later” to the date.


Expression Binding vs. Formatters

In this example, we use a complex expression binding to generate the required output. The statement uses predefined formatters for Message and Dates in combination with properties from the OData and i18n model. There is no strict guidance when to use a declarative expression binding or a custom formatter implementation. It is a matter of own programming style. The declarative expression is shorter and the code is not distributed across different files, however, debugging the expression is much harder than a formatter implementation.


Usage of CSS Parameters / Styles


HTML/CSS generated by UI5 is not part of the public API and may change in patch and minor releases. It’s common practice outside of SAP to override styles with the obligation to react to changes in newer versions. This approach does not work in the Fiori architecture as UI5 is a shared resource of all applications. As such, overriding UI5 with your own CSS in Fiori apps bears the risk of breaking your application when updating UI5. Never manipulate CSS via JavaScript. Wherever possible, use the predefined e.g. margin, padding classes, and the predefined theme parameters instead of hard coding a color in your own CSS, for example. A list of all available CSS classes is available in the UI5 documentation.

Finally, we need to define two new texts in i18n.properties for the form title and the expression binding.



File: i18n/i18n.properties

#~~~ Detail View ~~~~~~~~~~~~~~~~~~~~~~~~~~

#XTIT: Title of the General Information form

xtit.formTitle=General Information

#XFLD: Delivery date or later

xfld.andLater= {0} or

 

Now that we’re done with setting up the General Information form. We can focus on adding more information to the table.

In Detail.view.xml, replace the column definition with this enhanced one:



File: view/Detail.view.xml

<columns>

<Column>

<Text text="{i18n>detailLineItemTableIDColumn}"/>

</Column>

<Column minScreenWidth="Tablet" demandPopin="true" hAlign="Right">

<Text text="{i18n>detailLineItemTableUnitNumberColumn}"/>

</Column>

</columns>

<columns>

<!--Product description-->

<Column id="productColumn" width="30%">

<header>

<Label id="productLabel" text="{/#Z_C_PurchaseorderitemType/Product/@sap:label}"/>

</header>

</Column>

<!--Delivery Date-->

<Column demandPopin="true" id="deliveryDateColumn" minScreenWidth="Tablet" popinDisplay="Inline" width="15%">

<header>

<Label id="itemDeliveryDateLabel" text="{/#Z_C_PurchaseorderitemType/DeliveryDate/@sap:label}"/>

</header>

</Column>

<!--Ordered Quantity-->

<Column demandPopin="true" hAlign="Right" id="quantityColumn" minScreenWidth="Tablet" popinDisplay="Inline" width="15%">

<header>

<Label id="quantityLabel" text="{/#Z_C_PurchaseorderitemType/Quantity/@sap:label}"/>

</header>

</Column>

<!--Item price-->

<Column demandPopin="true" hAlign="Right" id="priceColumn" minScreenWidth="Tablet" popinDisplay="Inline" width="20%">

<header>

<Label id="priceLabel" text="{/#Z_C_PurchaseorderitemType/Price/@sap:label}"/>

</header>

</Column>

<!--Total value of the item-->

<Column demandPopin="true" hAlign="Right" id="grossAmountColumn" minScreenWidth="Tablet" popinDisplay="Inline" width="20%">
<header>

<Label id="grossAmountLabel" text="{/#Z_C_PurchaseorderitemType/GrossAmount/@sap:label}"/>

</header>

</Column>

</columns>

 

Our table now consists of 5 columns: Product Name, Delivery Time, Quantity, Price, Gross Amount. Because every column corresponds to a property with label annotations, we can use metadata data binding to label annotation instead of defining the label in i18n.properties. All date, quantity, and amount fields need to be right aligned, so we’ve changed the alignment.

We want to prevent the columns Delivery Time, Quantity, and Gross Amount to be hidden on small devices and instead shown as a popin. To do so, we set the demandPopin property to true. When popinDisplay equals Inline, we specify that the cell content is displayed next to the header in the same line. See more information in the documentation.

We've now redefined our column controls in the table, we need to adjust the Column List Item definition to match - specifically the content of the cells aggregation.



File: view/Detail.view.xml

<ColumnListItem id="columnListItem">


<cells>

<ObjectIdentifier title="{Product}" text="{POItemPos}"/>

<ObjectNumber

number="{

path: 'GrossAmount',

formatter: '.formatter.currencyValue'

}"

unit="{GrossAmountCurrency}"/>

</cells>

</ColumnListItem>

<ColumnListItem id="columnListItem">

<cells>

<!--Product description-->

<Text id="productCell" class="sapMTableContentMargin" text="{Product}"/>

<!--Delivery Date-->

<Text id="deliveryDateCell" text="{path:'DeliveryDate', type: 'sap.ui.model.type.Date', formatOptions: { style: 'medium'}}" class="sapMTableContentMargin"/>

<!--Ordered Quantity-->

<Text id="quantityCell" class="sapMTableContentMargin" text="{path: 'Quantity', type: 'sap.ui.model.type.Integer'}"/>

<!--Item price-->

<ObjectNumber class="sapMTableContentMargin" emphasized="false" id="priceCell" number="{parts: [ {path: 'Price'}, {path: 'PriceCurrency'}], type : 'sap.ui.model.type.Currency', formatOptions: { showMeasure: false }}" unit="{PriceCurrency}"/>

<!--Total value of the item-->

<ObjectNumber class="sapMTableContentMargin" id="grossAmountCell" number="{parts: [ {path: 'GrossAmount'}, {path: 'GrossAmountCurrency'}], type : 'sap.ui.model.type.Currency', formatOptions: { showMeasure: false }}" unit="{GrossAmountCurrency}"/>

</cells>

 

Finally, the text above the Product Items table still reads technical. This is because the template wizard did not find suitable, human readable texts. Let’s change these texts.



File: i18n/i18n.properties

 

#XBLI: Text for the to_PurchaseOrderItems table with no data

detailLineItemTableNoDataText=No to_PurchaseOrderItems

 

#XTIT: Title of the to_PurchaseOrderItems table

detailLineItemTableHeading=to_PurchaseOrderItems

 

#XTIT: Title of the to_PurchaseOrderItems table

detailLineItemTableHeadingCount=to_PurchaseOrderItems ({0})

 

#XBLI: Text for the to_PurchaseOrderItems table with no data

detailLineItemTableNoDataText=No Purchase Order Items

 

#XTIT: Title of the to_PurchaseOrderItems table

detailLineItemTableHeading=Purchase Order Items

 

#XTIT: Title of the to_PurchaseOrderItems table

detailLineItemTableHeadingCount=Purchase Order Items ({0})

We’re now finished enhancing our view definition. Run your code and resize your window. The application should adapt its content to different screen sizes. Starting from full screen with list and detail next to each other. If you shrink the window, the list disappears. If you click the menu area in the upper left corner, the list appears. If you click in the detail area, the lists disappears again.

I hope the fourth part to the tutorial has sparked your interest in the chapters to come. Next time, we’ll enable approve and reject buttons.
1 Comment