SAP Web IDE, SAP Cloud Platform, SAPUI5

Let’s build an Offline Hybrid UI5 Application from scratch – Part 1

In this blog post, I’m going to show how simple it is to build an offline app from scratch using the HAT on Web IDE. I will be using Ludo’s blog as reference so I would recommend you to go through his post first if you haven’t done so.

I’m going to assume you have basic UI5 knowledge, activated the required cloud platform services, configured the sample cloud platform destination and activated the HAT feature as per Ludo’s blog. One change that I’ll make, though, is change the destination URL such that it points to the demo SAP mobile platform instead of directly to the ESPM sample service.

URL: https://hcpms-<your account number>trial.hanatrial.ondemand.com instead of https://hcpms-<your account number>trial.hanatrial.ondemand.com//SampleServices/ESPM.svc

Feel free to use any destination if you already have a running OData service somewhere but ensure you replace the relevant property values in the examples below.

SAP Web IDE, SAP Cloud Platform, SAPUI5

Ensure you have the correct Basic Auth credentials by accessing the URL directly and logging in with the username and password.

Let’s get started!

First, create an application in Web IDE Full-Stack using the SAP UI5 Application Template

SAP Web IDE, SAP Cloud Platform, SAPUI5

SAP Web IDE, SAP Cloud Platform, SAPUI5

Configuring a Hybrid Mobile Application

Next, enable the app as a Hybrid Mobile Application. Right click on your project > Mobile > Enable as Hybrid Mobile Project.

SAP Web IDE, SAP Cloud Platform, SAPUI5

Note: If the ‘Mobile’ option is not on the list, check that you have activated the HAT feature in Web IDE.

Your project structure should now look like this. Additional details are available here.

SAP Web IDE, SAP Cloud Platform, SAPUI5

Add the “sap.mobile” section in the manifest.json file to trigger addition of the Kapsel Offline OData plugin during build.

“sap.ui5”: {…},
“sap.mobile”: {
“definingRequests”: {},
“stores”: []
}

Configuring the Data Source and the Offline Store

Next, let’s configure the data source and the offline store so that our app initializes an offline store (local DB) based on the OData service.

Data Source

In manifest.json, add a new model and a data source:

SAP Web IDE, SAP Cloud Platform, SAPUI5

SAP Web IDE, SAP Cloud Platform, SAPUI5

“dataSources”: {
“offlineService”: {
“uri”: “/mssampledata/offline/SampleServices/ESPM.svc/”,
“type”: “OData”,
“settings”: {
“odataVersion”: “2.0”,
“localUri”: “localService/metadata.xml”
}
}
}

Add a new route in neo-app.json. Ensure the target name is the same as the cloud platform destination name.

{
“path”: “/mssampledata/offline”,
“target”: {
“type”: “destination”,
“name”: “mssampledata”
},
“description”: “Sample Service”
}

Offline Store Creation

The below code snippets, taken from Ludo’s blog, will basically create and open the offline store before loading the component during first initialization.

Open the sap-mobile-hybrid.js file and replace sap.hybrid.startApp with sap.hybrid.openStore

if (“serverHost” in context && “serverPort” in context && “https” in context) {
// start SCPms logon
sap.hybrid.kapsel.doLogonInit(context, appConfig.appID, sap.hybrid.openStore);
} else {
console.error(“context data for logon are not complete”);
}

Next, let’s prepare the offline store creation in the openStore function:

openStore: function () {
jQuery.sap.require(“sap.ui.thirdparty.datajs”);
var properties = {
“name”: “offlineService”,
“host”: sap.hybrid.kapsel.appContext.registrationContext.serverHost,
“port”: sap.hybrid.kapsel.appContext.registrationContext.serverPort,
“https”: sap.hybrid.kapsel.appContext.registrationContext.https,
“serviceRoot”: fiori_client_appConfig.appID + “_mssampledata/SampleServices/ESPM.svc/”,
“definingRequests”: {
“productsSet”: “/Products/?$expand=StockDetails”
}
};
store = sap.OData.createOfflineStore(properties);
var openStoreSuccessCallback = function () {
sap.OData.applyHttpClient(); //Offline OData calls can now be made against datajs.
sap.hybrid.startApp();
}
var openStoreErrorCallback = function (error) {
alert(“An error occurred” + JSON.stringify(error));
}
store.open(openStoreSuccessCallback, openStoreErrorCallback);
},

A Closer Look

Let’s digress and take a closer look at what’s happening in the openStore function as it is essential in understanding offline store creation.

To create an offline store the sap.OData.createOfflineStore method is used. A ‘properties’ object is passed as an argument and the return type is a sap.OfflineStore object.

There are 2 important parameters in the properties object:

  1. serviceRoot – this identifies the root of an OData service relative to a destination in mobile services. The offline store will be created using the metadata of the OData service which the serviceRoot points to. A service root is unique to an offline store.
  2. Defining Requests – simply put, this property tells the offline store which Entity Sets should be populated with data and be made available offline. In our example above, the offline store will have the Product and Stock data available offline.

Once the offline store is created, it should then be ‘opened’ for offline access. This is done through the sap.OfflineStore.open method. When the offline store is successfully opened, the applyHttpClient and original startApp method are called – more on these in my next blog.

Building the UI

At this stage, our hybrid offline app setup is complete. Let’s create a simple UI for our app.

Let’s create a Stock App that let’s you view the current stock details of a specific product from a Product List. Let’s also add a Supplier List at the bottom.

Home.view.xml

<mvc:View controllerName=”zoffline.demo.OfflineDemo.controller.Home” xmlns:html=”http://www.w3.org/1999/xhtml” xmlns:mvc=”sap.ui.core.mvc”
displayBlock=”true” xmlns=”sap.m” xmlns:form=”sap.ui.layout.form”>
<App id=”idAppControl”>
<pages>
<Page title=”Stok App Offline Demo”>
<content>
<VBox alignItems=”Center”>
<form:SimpleForm id=”stockDetailForm” title=”Stock Details” layout=”ResponsiveGridLayout” labelSpanXL=”4″ labelSpanL=”4″ labelSpanM=”4″
labelSpanS=”12″ adjustLabelSpan=”false” emptySpanXL=”3″ emptySpanL=”3″ emptySpanM=”3″ emptySpanS=”0″ columnsXL=”1″ columnsL=”1″ columnsM=”1″
singleContainerFullSize=”false”>
<form:content>
<Label text=”Product Id”/>
<Text text=”{offline>ProductId}”/>
<Label text=”Quantity”/>
<Text text=”{path: ‘offline>Quantity’, type: ‘sap.ui.model.type.Decimal’}”/>
<Label text=”Last Updated”/>
<Text text=”{path: ‘offline>UpdatedTimestamp’, type: ‘sap.ui.model.type.Date’, formatOptions: { style: ‘short’, pattern: ‘dd/MM/yyyy’}}”/>
</form:content>
</form:SimpleForm>
</VBox>
<List items=”{offline>/Products}” headerText=”Product List” growing=”true” growingThreshold=”5″>
<ObjectListItem title=”{offline>Name}” type=”Active” press=”onItemPress”
number=”{ parts:[{path:’offline>Price’},{path:’offline>CurrencyCode’}], type: ‘sap.ui.model.type.Currency’, formatOptions: {showMeasure: false} }”
numberUnit=”{offline>CurrencyCode}”>
<firstStatus>
<ObjectStatus text=”{offline>Category}”/>
</firstStatus>
<attributes>
<ObjectAttribute text=”{offline>ProductId}”/>
<ObjectAttribute text=”{offline>LongDescription}”/>
</attributes>
</ObjectListItem>
</List>
<List items=”{offline>/Suppliers}” headerText=”Supplier List” growing=”true” growingThreshold=”5″>
<StandardListItem title=”{offline>SupplierName}” />
</List>
</content>
</Page>
</pages>
</App>
</mvc:View>

Home.controller.js

sap.ui.define([
“sap/ui/core/mvc/Controller”
], function (Controller) {
“use strict”;

return Controller.extend(“zoffline.demo.OfflineDemo.controller.Home”, {
onItemPress: function (oEvt) {
var oContext = oEvt.getSource().getBindingContext(“offline”);
this.getView().byId(“stockDetailForm”).bindElement({
path: oContext.getPath() + “/StockDetails”,
model: “offline”
});

}
});
});

Nothing fancy here, the code is exactly the same as that of an online application. when we access the app on a mobile device or in offline mode, the codebase stays the same, that’s the beauty of Offline OData.

Let’s test it online!

Now let’s make sure the app is working as a webapp in our desktop browser. Run the project as Web Application.

SAP Web IDE, SAP Cloud Platform, SAPUI5

Deploy the Hybrid Application

Time to deploy the project as a hybrid application in Mobile Services.
Right click on the project. Mobile > Build as Packaged App. I will not go into detail as Ludo’s blog already covers this step quite well.

SAP Web IDE, SAP Cloud Platform, SAPUI5

When build is complete, scan the QR code with your device’s QR code reader to download and install the application.

SAP Web IDE, SAP Cloud Platform, SAPUI5

Testing the Application

Open the mobile application and go through the usual login screens. A white screen appears for a few seconds while the offline store is syncing. At this point, the offline store is getting created and the local database tables whose entity sets were defined in the defining request are getting populated with data (remember the openStore function?). After a few seconds, the home screen should load.

Congratulations, you’ve just created an offline application from scratch! Go to airplane mode and test it yourself.

SAP Web IDE, SAP Cloud Platform, SAPUI5

Supplier List empty?

As you can see below, the supplier list does not have any records. This is because we did not include ‘Suppliers’ in the defining requests and hence the Supplier data was not fetched and loaded to the offline store. To fix this, simply add a new defining query for “/Suppliers” in the defining request.

SAP Web IDE, SAP Cloud Platform, SAPUI5

Leave a Reply

Your email address will not be published. Required fields are marked *