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 tenth 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 chapter, we adapted the detail screen of the app.

In this chapter, we enabled approve and reject buttons.

In this sixth chapter, we set up a new model that stores the global app state.

In this seventh chapter,  we encapsulated approve / reject service calls.

In this eighth chapter, we mimicked the backend logic.

In this ninth chapter, we refreshed the master and detail screen.

In this tenth chapter, we’re going to implement code to block the screen for further input, for example, to prevent a user from approving the same purchase order twice.

Block further actions when approval is pending
Once the user presses either “approve” or “reject”, we need to block the screen for further input, for example, to prevent a user from approving the same purchase order twice. Swiping on the master list is a little different. Because it does not require the purchase order to be displayed in the detail screen.

The global model contains a property isBusyApproving that stores the information as to whether the purchase order displayed is waiting for the business logic to finish.

In Detail.view.xml, we'll replace the simple binding of the busy property by an expression binding as follows.



File: view/Detail.view.xml

<semantic:DetailPage

id="page"

navButtonPress="onNavBack"

showNavButton="{device>/system/phone}"

title="{i18n>detailTitle}"

busy="{detailView>/busy}"

busy="{= (${detailView>/busy} || ${globalProperties>/isBusyApproving})}"

busyIndicatorDelay="{detailView>/delay}">

This blocks the detail screen in case an approval is running in background. And, we also do this on the root view:



File: view/App.view.xml

<mvc:View

controllerName="acme.purchaseorder.controller.App"

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

displayBlock="true"

busy="{appView>/busy} "

busy="{= ${appView>/busy} || ${globalProperties>/isBusyApproving} }"

busyIndicatorDelay="{appView>/delay}"

xmlns="sap.m">

<SplitApp id="idAppControl" />

</mvc:View>

Next, we need to block swipe in the master list in case an approval is already running from the detail screen or via a previous swipe operation. We have already an empty function for the swipe event which we can now use:



File: controller/Master.controller.js


onSwipe: function(oEvent) {

// intentionally left empty

if (this.getModel("globalProperties").getProperty("/isBusyApproving") || this.getApprover().isSwipeApproving(

getIdForItem(oEvent.getParameter("listItem")))) {

oEvent.preventDefault();

}

},

We need to prevent the approval / rejection dialog from being opened in case a purchase order is being processed. We do this by simply calling the resolve function of the promise object.



File: controller/SubControllerForApproval.js


openDialog: function(bApprove, aPurchaseOrders) {

var fnPromise = function(fnResolve, fnFailed) {

if (this._oGlobalModel.getProperty("/isBusyApproving")) {

fnResolve(false);

return;

}

var sApprovalText, sTitle;

this._fnApproveActionFinished = fnResolve;

this._fnApproveFailed = fnFailed;

this._bApprove = bApprove;

You might experience no busy indicator running your application. This is because although /isBusyApproving is set and the binding busy="{= ${appView>/busy} || ${globalProperties>/isBusyApproving} }" is true, the busy indicator and blocking of the screen is done with a delay. You can receive the standard value using getBusyIndicatorDelay() from any view. The standard template changes the busy indicator delay from the standard value to 0 and back, for example, when loading metadata.
With the implementation of code to block the screen for further input finished, let's take a quick look at how to create extension points before moving on to backend implementation.


Create extension points


Let’s add extension points to the views.  Extension points enable the extensibility of our application by inserting custom content at the location of these extension points. This is done at certain places of XML views where it is likely that customers may want to insert custom content. We use the <ExtensionPoint> tag which serves as an anchor for extensions. Note that the <ExtensionPoint> will be replaced by whatever controls the customer provides, so make sure to place extension points where controls could be placed and to document which types of controls would be suitable.

We’ll add two extension points to the detail screen. One after the header and one after the form. This enables customers to add new fields to both parts of the screen.



File: views/Detail.view.xml

<mvc:View

controllerName="acme.purchaseorder.controller.Detail"

xmlns="sap.m"

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

xmlns:l="sap.ui.layout"

xmlns:core="sap.ui.core"

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

xmlns:semantic="sap.m.semantic"

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

[…]

</ObjectHeader>

<core:ExtensionPoint name="extensionAfterObjectHeader"/>

<form:SimpleForm class="sapUiForceWidthAuto sapUiResponsiveMargin" columnsL="1" columnsM="1" emptySpanL="5" emptySpanM="5"

[…]

</form:SimpleForm>

<core:ExtensionPoint name="extensionAfterForm"/>

</l:VerticalLayout>

Now we can mimic an extension of our app. We’ll create a new extension project based on our app. From the top menu, click File --> New --> Extension Project, click Next on the following screen, and open the app in the extension pane.



You’ll probably get an error message in the preview window. This is because the app isn’t launched in sandbox mode, but rather with a real backend, which is something that we haven’t configured in our app yet. You can run the app using mock data though. From the top menu, select Tools --> Extensibility Pane with Mock Server.

We can now use the predefined extension point to extend our app:



Click Extend --> Extend View/Fragment on the bottom of the Outline pane and open the code editor. We’ll just add a simple text field to illustrate the mechanics of XML view extensibility in UI5.



<core:FragmentDefinition xmlns:core="sap.ui.core" xmlns="sap.m"

[…]

<Text id="myExtension" text="My Extension"/>

</core:FragmentDefinition>

The result should look something like the following:


You can find more examples in the SAP UI5 documentation about view extensions.

I hope the tenth part of the tutorial has sparked your interest in the chapters to come. Next time, we’ll start the implementation of the OData service used for the purchase approval app.