Technical Articles
Step 9 with SAP Cloud SDK: Implement and Deploy a Frontend Application
This tutorial will show how to create a SAPUI5 frontend based on the services of the earlier blog posts. In particular, it will show how to:
-
- Create a new SAPUI5 project using the template
- Use SAPUI5 controls to create the view
- Consume the REST service
- Deploy the application to Cloud Foundry
- Using a monolithic approach
- Using a micro service approach
The resulting application will show a list of business partners from the ERP that is connected to the backend.
Note: This post is part of a series. For a complete overview visit the SAP Cloud SDK Overview.
Create a new SAPUI5 Project Using the Template
The easiest way to start developing a SAPUI5 application is to utilize the project templates of the WebIDE (https://www.sap.com/developer/topics/sap-webide.html). To access it, you need to create an account on SAP Cloud Platform as described in Step 2 of the SAP Cloud SDK Overview. After creating an account, you can activate and go to the WebIDE from the services tab. To create a new template-based project, select File > New > Project from Template and select SAPUI5 Application and create a new project using the wizard. For the purposes of the tutorial, all the default settings are fine. In the following code snippets, we will assume that the project was named sdk-tutorial-frontend. If you copy the code and have used a different project name, make sure to replace sdk-tutorial-frontend with the project name you have chosen during the wizard
The project will automatically have the following structure:
The webapp folder contains the actual application which follows the Model View Controller pattern and therefore is split into the View1.view.xml, models.js, and View1.controller.js. Also, there is a separate style.css file, which can be used to customize the look of your app (by default all controls will be Fiori compliant). Finally, there is the i18n.properties file. I18n stands for internationalization and the file itself will serve as a base for translation of your app. For the sake of simplicity, we will not use i18n to define the UI texts in this tutorial. You can read more about it in the SAPUI5 documentation.
The other files contain configuration or are responsible for loading and initializing SAPUI5. Before touching the actual app, it is recommended to update the index.html to fetch the SAPUI5 sources from the official CDN. To do that, you need to change it in the script tag in line 11 as follows:
<script id="sap-ui-bootstrap"
src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
data-sap-ui-libs="sap.m"
data-sap-ui-theme="sap_belize"
data-sap-ui-compatVersion="edge"
data-sap-ui-resourceroots='{"sdk-tutorial-frontend": ""}'>
</script>
You can use this link to the latest stable version of SAPUI5 for the tutorial or for testing purposes, but avoid it for productive use. In an actual app, you always should specify an SAPUI5 version explicitly.
How to use SAPUI5 Controls to Create the View
When opening the view, the WebIDE gives 2 options, the layout editor, and the code editor. The code editor is the more powerful tool which gives you the ability to change your code directly, while the layout editor shows you a visual editor, providing an approximated preview of your changes, and uses drag and drop of a control catalog to edit your view. You can use either editor to follow this tutorial, but we will show the actual code as you would see in the code editor since it is easier to compare and avoid mistakes this way.
To get an overview of what controls SAPUI5 provides, you can use the SAPUI5 Explored website to see a searchable list with interactive examples.
For our app, we want to display a list of business partners, so a quick search in Explored suggests that we should use sap.m.List control. Controls are organized in libraries and in the beginning, you will mostly use controls from sap.m and sap.ui.core. These controls are by default responsive and will adjust to fit the device that displays the website.
When you open the View1.view.xml, it will already contain the basic structure needed for a SAPUI5 application. In the following code, you only need to copy the List tag and the StandardListItem and put it between the content tag in your own View1.view.xml.
This will already create the list we want to display, but of course, there is no data loaded yet. This will be explained in the next part.
<mvc:View controllerName="sdk-tutorial-frontend.controller.View1"
xmlns:html="http://www.w3.org/1999/xhtml" xmlns:mvc="sap.ui.core.mvc"
displayBlock="true" xmlns="sap.m">
<App>
<pages>
<Page title="Business Partner Explorer">
<content>
<!-- Add this between the content tags -->
<List headerText="Business Partners"
items="{businessPartner>/}">
<StandardListItem
title="{businessPartner>LastName}, {businessPartner>FirstName}"
description="ID: {businessPartner>BusinessPartner}"/>
</List>
</content>
</Page>
</pages>
</App>
</mvc:View>
Consuming the REST Service
In the previous steps of this tutorial series, we already created a REST Service that requests and returns a list of business partners from SAP S/4HANA. To call this service and connect its data to our list, we use jQuery and SAPUI5’s JSONModel.
This business logic will be implemented as part of the controller. As with the view, the template already provides some basic structure.
To add the functionality, we need to use jQuery.get() to retrieve the data. Because of the asynchronous nature of the call, we need to use the done callback to put the data in a JSONModel and connect it with the view. Additionally, we need to include the JSONModel by adding it as the first parameter of the sap.ui.define() function. You can also add jQuery there, but SAPUI5 already guarantees that jQuery is included everywhere. Adapt the controller View1.controller.js as follows.
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/model/json/JSONModel"
], function(Controller, JSONModel) {
"use strict";
return Controller.extend("sdk-tutorial-frontend.controller.View1", {
onInit: function () {
var view = this.getView();
jQuery.get("/businesspartners")
.done(function (data) {
var model = new JSONModel(data);
view.setModel(model, "businessPartner");
});
}
});
});
Now, this simple app is already completed. To try it out, we can use the WebIDE’s deployment or follow the rest of the tutorial to deploy the app on Cloud Foundry. To use the WebIDE deployment, just right-click the project folder on the left and select Run > Run As > Web Application. If we use this method, it will not be connected to our REST Service as they are in different environments that can’t communicate. Instead of looking at an empty list, we can replace the .done() callback with the code below which uses dummy data so you can verify the functionality of the app without a backend. Just make sure to revert this change once you deploy on Cloud Foundry.
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/model/json/JSONModel"
], function(Controller, JSONModel) {
"use strict";
return Controller.extend("sdk-tutorial-frontend.controller.View1", {
onInit: function () {
var view = this.getView();
jQuery.get("/businesspartners")
.always(function () {
var data = [{
"BusinessPartner": "1",
"FirstName": "John",
"LastName": "Doe"
}, {
"BusinessPartner": "2",
"FirstName": "Jane",
"LastName": "Roe"
}, {
"BusinessPartner": "3",
"FirstName": "John",
"LastName": "Smith"
}, {
"BusinessPartner": "4",
"FirstName": "Carla",
"LastName": "Coe"
}];
var model = new JSONModel(data);
view.setModel(model, "businessPartner");
});
}
});
});
After you implemented these steps, the application should look like this:
Make sure to replace the .always() callback with .done() from the previous snippet once you deploy on Cloud Foundry. Afterwards, download the project files by right-clicking the project and selecting Export. It will download a .zip archive, that can be easily extracted.
Deploy the Application to Cloud Foundry
Using the SAP Cloud Platform Cloud Foundry for your deployment gives several benefits. One of these benefits is the flexibility to structure the project as a monolith or split it into several micro services. In previous blog posts, we already showed how to develop and deploy an API service to Cloud Foundry as well as Neo. If you have not setup the backend yet, you can follow the other tutorials in this series which are linked in the SAP Cloud SDK Overview.
In the following, we want to explore how to deploy a full web application that uses data provided by our service. We will show both the monolithic approach as well as a more micro service oriented architecture.
Deploy the Application to Cloud Foundry Using a Simple Monolith Approach
Prerequisites
- You finished step 4 in the series https://blogs.sap.com/2017/05/21/step-4-with-sap-s4hana-cloud-sdk-calling-an-odata-service/
Deploying the whole application including backend and frontend in a monolithic approach can be very easy. In order to have a better understanding of our final goal look at the picture:
We utilize the same server to serve both the service URL’s as well as the frontend files. To do this we extract the downloaded archive and copy theses files to the webapps folder of the backend.
cd <PATH_TO_DOWNLOADED_ZIP>
unzip ui5-webapp.zip
cp -a ui5-webapp/webapp/* /path/to/java/application/application/src/main/webapp/
To deploy the application to CloudFoundry, a proper manifest.yml is needed, as well as other configuration outlined in step 4.
Finally, we build the application and deploy to CloudFoundry:
mvn clean install
cf push
If your deployment was successful, the Cloud Foundry CLI will show you information about the deployment, that looks like the following snippet.
requested state: started
instances: 1/1
usage: 512M x 1 instances
urls: firstapp-USERNAME.cfapps.sap.hana.ondemand.com
last uploaded: Fri Jul 28 12:00:36 UTC 2017
stack: cflinuxfs2
buildpack: sap_java_buildpack
To verify that the deployment was successful, you can enter the URL in your browser and see the application showing the list of business partners.
This approach allows for a quick initial deployment and a simple deployment process. For small applications like this business partner explorer, this approach will be preferable, but as the application grows and needs to scale it will get progressively harder and more expensive. Frontend and backend cannot be decoupled and scaled individually, also to make changes to either you need to deploy both which will result in more downtime unless you use blue green deployments.
Deploy the Application to Cloud Foundry Micro Service Approach
Prerequisites
- you have finished and understood step 7 in the series: https://blogs.sap.com/2017/07/18/step-7-with-sap-s4hana-cloud-sdk-secure-your-application-on-sap-cloud-platform-cloudfoundry/
We assume that you know what the Approuter is for and know the basics of how to configure it.
The micro service approach decouples the separate components and allows scaling them individually. To do this separation, we need to run the frontend in its own container. We deploy the frontend application completely separate in its own container. For that, we use the staticfile_buildpack which is provided by default in Cloud Foundry. In the following, we will show how this can be done.
As in the monolithic approach, here is a picture of our final setup:
As the frontend will be its own service now, it should be in a separate folder from the backend (instead of being part of the backend as with the monolithic approach). Once you moved the code to a separate folder, you need to add a manifest.yml as it will need a different Cloud Foundry configuration than the backend now.
Like in the monolithic step we start with unpacking the downloaded zip.
cd <PATH_TO_DOWNLOADED_ZIP>
unzip ui5-webapp.zip
cd ~
mkdir firstapp-frontend
cd firstapp-frontend
cp -a <PATH_TO_DOWNLOADED_ZIP>ui5-webapp/webapp/* .
Now create here a new manifest.yml with the following content:
---
applications:
- name: firstapp-frontend
memory: 64M
host: firstapp-frontend-USERNAME
buildpack: staticfile_buildpack
Push the frontend to CloudFoundry:
cf push
When you point your browser to firstapp-frontend-USERNAME.cfapps.sap.hana.ondemand.com you should see the application running but without data. To connect the frontend to our previous deployed backend we need a proper configured approuter.
Install the AppRouter like in step 7 or just reuse your existing installation.
As for all application that you want to deploy on CloudFoundry, we need a manifest file. You also need to add two routes to the AppRouter. The achieve this, adjust the manifest.yml and xs-app.json of the AppRouter. Make sure that the destination names and URLs match your own deployment. The name for the destinations is arbitrary. You can choose them on your own. We will use them in the next step to point our wished URL to the actual one.
---
applications:
- name: approuter
host: approuter-p1942765239trial
memory: 128M
path: approuter
buildpack: nodejs_buildpack
env:
TENANT_HOST_PATTERN: 'approuter-(.*).cfapps.sap.hana.ondemand.com'
destinations: '[
{
"name":"business-partner-api",
"url" :"https://firstapp-USERNAME.cfapps.sap.hana.ondemand.com/",
"forwardAuthToken": true
}, {
"name":"business-partner-frontend",
"url" :"https://firstapp-frontend-USERNAME.cfapps.sap.hana.ondemand.com/"
}
]'
services:
- my-xsuaa
Within the xs-app.json we configure which URL path shall be handled and resolves to what destination. For instance: url.to.router/businesspartners resolves to url.in.CF/businesspartners. If you want to know more on that go back to step 7 and read there.
{
"welcomeFile": "index.html",
"routes": [{
"source": "^/businesspartners",
"destination": "business-partner-api"
},
{
"source": "/",
"destination": "business-partner-frontend"
}]
}
Finally, just run the Cloud Foundry CLI to deploy the AppRouter, the backend, and the frontend.
cd approuter/
cf push
cd ../firstapp-frontend
cf push
After you deployed all 3 components, the application should be available just like before.
Since we didn’t touch the deployed backend from the monolithic step it should run right away. You can safely remove the frontend code from the webapps folder in the backend and redeploy.
To summarize, the micro service approach allows you to independently develop, deploy and scale the applications but also introduced operational complexity. A monolithic approach has lower operational complexity and is faster to setup initially but can run into scaling issues later. To choose the right way for you, you will have to evaluate the size and scope of your project and how the different approaches will help you achieve your goal.
Hi Frank,
I'm confused with the section Consuming the REST Service and not sure where you set destination to consume the service like we usually develop SAPUI5 apps.
Could you please clarify that?
Thanks
Tri
One more question:
Could we call oData request inside the app?
Thank you.
Hello Tri,
you can do everything inside the app that you can do in SAPUI5, as it is a standard UI5 app. In this blog post, we focus on the integration with the service developed as part of the tutorial, which is a plain REST service.
This could also be an OData service - there is also an OData provisioning framework for SAP Cloud Platform that you can integrate with the SAP S/4HANA Cloud SDK.
Best regards,
Henning
Hi Frank,
Thanks for posting such great blog. I am trying to get some hands-on experience with this concept. I implemented the steps as you mentioned in the blog when I run the application, It displays the blank rows as below screenshot. Can you please tell me what would be the reason for this?
Thanks,
Sankeerth
As clarified via email: you had some typos in your template bindings.
Change log (December 22, 2017):
Change log (January 8, 2018):
Hello.
I have read all of your previous blogs and I wanted to ask you if it is possible to deploy the front end application to Neo Environnement?? What do I have to change?
Hello Jaglika,
you should be able to deploy the monolith approach to Neo without major changes. Instead of cf push, use the corresponding commands introduced in the appendix of tutorial step 2.
The Microservice approach is specific to Cloud Foundry.
I made it work on Neo.
I have tried to deploy the application web frontend to Neo Environment. But I get an error when trying to get the data via jquery.get where the url is "/salesorders" because I have implemented this entity from GWSAMPLE_BASIC/SalesOrder through en ErpEndpoint which is on https://sapes5.sapdevcenter.com .
When I start the application via Neo it works, I can get the backend data, but for my frontend it does not work with the url: /salesorders. I tried to change it to: /firstapp-application/salesorders and then I get the status 200 (OK), but when trying to expose the data on my frontend (sapui5 application created at WEB IDE and exported to the folder firstapp/application/src/main/webapp/) I see only empty rows. Thank you in advance.
As far as I understand, your backend is properly working, but the frontend is calling a different path. Did you take a look at the developer console of your browser to see the actual network requests made by your frontend application - maybe you can post a screenshot. Also pay attention to any potential errors shown on the console.
This is the view:
This is the controller:
It is strange that when I have set the variable url only to "/salesorders" I got an error 404 in my console, but when I changed it to "firstapp-application/salesorders" I got a response 200 but no data is shown.
This is the web app that is started from Neo Environment:
I hope this can halp more.
Regards,
Jaglika
Hello Jaglika,
I see in your view.xml that there are additional single quotes around in data binding of items. Please try with the following.
Best regards,
Henning
Hello Henning. Can you please check my reply to your comment? Thanks in advance.
Jaglika
Hello Henning,
The trial version allows us to have only 2 instances of running applications. Is there a way we can achieve the microservices section shown here using lets say 2 different trial accounts?
Regards
Arun.
Hello Arun,
instead of trying to make it work with different trial accounts, I would suggest that you use a slightly different approach where you host the frontend as part of the approuter. The approuter also supports serving static content, with a router configuration similar to the following (assuming that requests to /frontend-path/ should be served with static content available from folder static-content/frontend-path/):
Best regards,
Henning
Hello Henning,
I am trying to deploy the frontend as a micro service.
My service provides an odata-based endpoint.
I am able to call the odata-service successfully through the approuter: <approuter>/service/$metadata.
Starting the frontend through the approuter forwards me to <frontend-application>/webapp/index.html (Browser redirect).
However, the frontend then try to call the $metadata from <frontend-application>/service/$metadata instead of <approuter>/service/$metadata which leads to 404 not found.
Please let me know the following:
Kind regards
Oliver
Hello Oliver,
the approuter should not visibly redirect the browser to the <frontend-application>. It should stay at <approuter>. Please check your xs-app.json configuration
Best regards,
Henning
Thanks Henning,
I had a target specified within xs-app.json which lead to the described bahivour.
It works now.
Thanks and regards,
Oliver
This actually represents something you shouldn't do right? You are writing your own Java backend to interpret the OData Service when you could just be exposing the original odata feed to the Fiori client using something like elements.
Or can't you call the S4HANA Cloud OData directly?
Hi Joao,
this is a very minimal example to highlight the capabilities of the SAP S/4HANA Cloud SDK for interacting with OData services.
Depending on your requirements, just a Fiori app that accesses OData services directly may be an alternative. If you need some kind of business logic, or combine this with data from other sources, or have other requirements regarding security, having a dedicated backend for your application becomes necessary. In that case, the SAP S/4HANA Cloud SDK is there to help you.
Best regards,
Henning
Change log May 2019: update name to SAP Cloud SDK (background)