Technical Articles
Designing UI5 Apps for SAP Launchpad Service – Part 1
Content Navigation
Part 1: A simple UI5 app that runs in the SAP Launchpad Service (Current post)
Part 2: Multiple Apps with a Shared Reuse Library
Part 3: Splitting bigger projects
Introduction
With this guide, we want to give you technical insights into UI5 apps in the SAP BTP that run in the launchpad service.
You will learn how to structure complex UI5 applications to avoid common pitfalls.
You should already be familiar with:
- SAPUI5 and UI5 Tooling
- SAP UI5 App Descriptor
- SAP Business Application Studio
- SAP Fiori tools– Multi Target Applications (MTA) especially the syntax of mta.yaml
- HTML5 Repository and Managed Application Router and xs-app.json
- SAP Authorization and Trust Management Service (XSUAA) and xs-security.json
- Destination service and destinations
- SAP Launchpad service
This guide will not go into details about the following:
– Templating tools for UI5 projects and MTA
– Developing service backends e.g. with CAP
– Configuration of launchpad sites
– OData and SAP Fiori elements
All discussed samples are published in a Github repository.
Related Blogs
- Using UI5 Libraries in CF approuter (Wouter Lemaire)
- SAP Fiori Development with SAP Business Application Studio (Yuval Morad)
- Achieve Separate Lifecycles for Managed Approuter Applications with SAP Business Application Studio (Yuval Morad)
- Cross-MTA Dependencies. Custom UI5 Library Reference Example. (Bradley Smith)
A Simple UI5 app that runs in the SAP Launchpad Service
This first post will focus on a simple deployment which only contains one standalone UI5 app, that fetches data from a backend. This app can run in the launchpad or standalone.
The sample code can be found in the Github repository.
Deployed Content
In the beginning let’s have a look at all the parts which form a deployment of an app.
There are mainly 3 service instances that you can find in your Cloud Foundry space after deploying it:
![]() |
|
MTA Project Structure
The deployment of this is described in the mta.yaml of the project. It is structured as follows:
- 3 resources that represent the created service instances. They are all created by this MTA and are therefore managed services.
- A module for the UI5 app. Most build steps are defined in the package.json. As a result it produces a zip file.
- A module that deploys the app to the app-host. It declares a build dependency to the UI5 app to include the produced zip and requires the app-host resource as the deploy target.
- A module that creates the destinations. It requires the destination service resource as content target and the app-host and XSUAA instance to create destinations for them.
The configuration defines the destinations and adds some additional properties.
The configuration of the destination resource enables it to use the managed app router and already creates a hardcoded destination for the simple OData service (northwind):
- name: btp-samples-simple-app-dest-srv
type: org.cloudfoundry.managed-service
parameters:
config:
HTML5Runtime_enabled: true
init_data:
instance:
destinations:
- Name: northwind
Authentication: NoAuthentication
ProxyType: Internet
Type: HTTP
URL: https://services.odata.org/V3/Northwind/Northwind.svc/
existing_destinations_policy: update
version: 1.0.0
service: destination
service-name: btp-samples-simple-app-dest-srv
service-plan: lite
- name: btp-samples-simple-app-uaa
type: org.cloudfoundry.managed-service
parameters:
path: ./xs-security.json
service: xsuaa
service-plan: application
service-name: btp-samples-simple-app-xsuaa-srv
The destination content module requires all 3 instances. It specifies to create service keys which will provide the credentials.
- name: btp-samples-simple-app-dest-content
type: com.sap.application.content
requires:
- name: btp-samples-simple-app-dest-srv
parameters:
content-target: true
- name: btp-samples-simple-app-repo-host
parameters:
service-key:
name: btp-samples-simple-app-repo-host-key
- name: btp-samples-simple-app-uaa
parameters:
service-key:
name: btp-samples-simple-app-uaa-key
Then it defines the destinations to be created. Here the values of the ServiceInstanceName properties are the instance names and not the MTA resource names as it is the case above.
It adds some additional properties. First it specifies the same service name for XSUAA and app-host. This way they are bound together.
Second, it defines the authentication method for the app-host for further routings to use OAuth2UserTokenExchange via the XSUAA:
parameters:
content:
instance:
destinations:
- Name: btp-samples-simple-app_repo_host
ServiceInstanceName: btp-samples-simple-app-html5-srv
ServiceKeyName: btp-samples-simple-app-repo-host-key
sap.cloud.service: btp.samples.simple.app
- Name: btp-samples-simple-app_uaa
ServiceInstanceName: btp-samples-simple-app-xsuaa-srv
ServiceKeyName: btp-samples-simple-app-uaa-key
sap.cloud.service: btp.samples.simple.app
Authentication: OAuth2UserTokenExchange
existing_destinations_policy: ignore
The app-content module collects the zip of the UI5 app module at build time.
Later it deploys them to the app-host.
- name: btp-samples-simple-app-app-content
type: com.sap.application.content
path: .
requires:
- name: btp-samples-simple-app-repo-host
parameters:
content-target: true
build-parameters:
build-result: resources
requires:
- name: btpsamplessimpleapp
artifacts:
- btpsamplessimpleapp.zip
target-path: resources/
The module of the app itself configure the build via node.js commands.
- name: btpsamplessimpleapp
type: html5
path: simple.app
build-parameters:
build-result: dist
builder: custom
commands:
- npm install
- npm run build:cf
supported-platforms: []
The UI5 App
Build
The UI5 app is built with UI5 Tooling. The build is controlled via the package.json and the ui5-deploy.yaml and produces a zip for the content module packager.
Routings
There is an xs-app.json file that defines routings to backend services.
Here the simple OData service path is forwarded via the northwind destination, which is created by the MTA, without any authentication.
Usually you would connect it to a BTP cloud service and define an additional authentication flow like OAuth2UserTokenExchange.
{
"welcomeFile": "/index.html",
"authenticationMethod": "route",
"logout": {
"logoutEndpoint": "/logout",
"logoutPage": "/logout-page.html"
},
"routes": [
{
"source": "^/northwind/(.*)$",
"target": "/$1",
"authenticationType": "none",
"destination": "northwind"
},
{
"source": "^/index.html",
"service": "html5-apps-repo-rt",
"cacheControl": "no-cache, no-store, must-revalidate"
},
{
"source": "^/logout-page.html$",
"service": "html5-apps-repo-rt",
"authenticationType": "none"
},
{
"source": "^(.*)$",
"target": "$1",
"service": "html5-apps-repo-rt",
"authenticationType": "xsuaa"
}
]
}
There is a special route for the index.html which is also specified as welcomeFile. This route disables browser caching to ensure the page is reloaded and an authentication flow can be triggered if necessary.
Another special route is for the logout-page.html which is also used in the logout configuration. It disables authentication for the page to prevent that another login flow is triggered after logout.
You can test the app locally via the UI5 Tooling. It provides a local middleware which is configured in ui5.yaml.
Here similar routings are configured e.g. the same OData service is connected. See Use Custom Middlewares for more details.
server:
customMiddleware:
- name: fiori-tools-proxy
afterMiddleware: compression
configuration:
ignoreCertError: false # If set to true, certificate errors will be ignored. E.g. self-signed certificates will be accepted
backend:
- path: /northwind
pathPrefix: /
url: https://services.odata.org/V3/Northwind/Northwind.svc/
ui5:
path:
- /resources
- /test-resources
url: https://ui5.sap.com
version: # The UI5 version, for instance, 1.78.1. Empty means latest version
To start the local test just run the start script in the package.json.
There is also a start-local script which runs the app in a local FLP.
App Descriptor
It has a manifest.json file that defines a unique sap.app/id and application sap.app/type for it.
Having unique Ids for all apps in your subaccount is important to avoid conflicts.
"sap.app": {
"id": "btp.samples.simple.app",
"type": "application",
"i18n": "i18n/i18n.properties",
"title": "{{appTitle}}",
"description": "{{appDescription}}",
"applicationVersion": {
"version": "1.0.0"
}
}
An entry under sap.app/crossNavigation/inbound defines the tile and navigation for the launchpad service. It is important to use unique values here to avoid conflicts with other apps which are deployed in the same subaccount.
"crossNavigation": {
"inbounds": {
"btp-samples-simple-app-inbound": {
"signature": {
"parameters": {},
"additionalParameters": "allowed"
},
"semanticObject": "simpleApp",
"action": "display",
"title": "{{flpTitle}}",
"subTitle": "{{flpSubtitle}}",
"icon": ""
}
}
}
The OData service URL is specified under sap.app/dataSources. It points to the routed source defined in xs-app.json. Please note that this is a relative path, which is resolved relative to the app. The reason for this is explained below.
"dataSources": {
"mainService": {
"uri": "northwind",
"type": "OData"
}
}
It also specifies sap.cloud/service with the same service name as specified for the destinations in the mta.yaml. Note that this has a different meaning than the sap.app/id (although it has the same value in this sample).
"sap.cloud": {
"public": true,
"service": "btp.samples.simple.app"
}
Running the Deployed App
Deploying the App
To deploy the app you have to build the MTA and then deploy it to your Cloud Foundry space.
SAP Business Application Studio simplifies this for you, because the build tool and the deployer are already set up.
The deployment itself will run quite fast because only service instances and content are deployed. There is no CF application at all.
Running Standalone
After the deployment the app can be run standalone from its index.html via the managed approuter. You can find it in the SAP BTP cockpit on the HTML5 Applications page under your subaccount. Its URL will look like this:
https://<tenant subdomain>.launchpad.cfapps.<landscape host>/<destination instance guid>.<service name>.<sap.app/Id>-/index.html
Service name and sap.app/id have any dots removed. Example for a URL:
https://xyz.launchpad.cfapps.eu10.hana.ondemand.com/8b1f65d4-0445-446f-a4b5-90627416789e.btp-samples-simple-app.btpsamplessimpleapp-1.0.0/index.html
All routings in xs-app.json are relative to this. For example, the OData service is under
https://xyz.launchpad.cfapps.eu10.hana.ondemand.com/8b1f65d4-0445-446f-a4b5-90627416789e.btp-samples-simple-app.btpsamplessimpleapp-1.0.0/northwind/
There is no way to install a routing directly in the app router root. Therefore, it is important that you always use paths that are relative to the app location.
The index.html which is located next to the app defines the resource mapping for it and points to the same folder:
<script id="sap-ui-bootstrap"
src="https://ui5.sap.com/resources/sap-ui-cachebuster/sap-ui-core.js"
data-sap-ui-resourceroots='{
"btp.samples.simple.app": "./"
}'
data-sap-ui-libraries="sap.m" >
</script>
Inside the manifest.json the data source path is automatically resolved relative to the app.
The index.html loads UI5 via a public CDN URL which has cachebuster enabled. This ensures the best startup performance, because the resources will be fetched from the closest location and cached in the browser cache.
Running in the Launchpad Service
In order to run the app in the Launchpad Service, you have to configure it first and assign it to a user role. You can find more details on this here: Integrate Your SAPUI5 App into Your Launchpad Site
After everything is set up the app could be launched directly via an URL with this pattern:
https://<tenant subdomain>.launchpad.cfapps.<landscape host>/site?siteId=<site ID>#<semantic object>-<action>, for example:
https://xyz.launchpad.cfapps.eu10.hana.ondemand.com/site?siteId=df526ffe-2a32-464e-8fc5-5d5db908334e#simpleApp-display
As you can see, the URL points to a completely different path than where the app can be found. Also the startup HTML page does not contain the resource mapping as in the standalone case.
In order to launch the app, the platform provides all needed URLs. This information is retrieved via a request which looks like
/comsapfdc/fdcCache_<subaccountid (- replaced by _)>/~<cache token>~/apps/<sap.app/id>/ui5AppInfo.json
You can find this in the network trace when starting the app.
In this data you can find a similar URL as when running the app standalone, but it has an additional cache token, which is the date of the last change.
{
"name": "btp.samples.simple.app",
"url": "/8b1f65d4-0445-446f-a4b5-90627416789e.btp-samples-simple-app.btpsamplessimpleapp/~211021122629+0000~/",
"manifest": true,
"asyncHints": {
"libs": [{
"name": "sap.f",
"lazy": false
}, {
"name": "sap.m",
"lazy": false
}, {
"name": "sap.ui.fl",
"lazy": false
}],
"components": []
},
"messages": [],
"version": "1.0.0"
}
As mentioned before, some hardcoded URLs would be resolved relative to the document location. In this case this would not result in the correct URL for fetching the logo image.
Therefore the URL is calculated in the Master.controller.js as follows:
sap.ui.require.toUrl("btp/samples/simple/app/images/logo_ui5.png")
The same applies to any other call that you do for example any REST call that you trigger from your coding.
This way your app is independent of the location from where it is launched.
Next Steps
Now you are ready to plan your first simple app project.
However, usually you need more apps and reuse components.
Please check out part 2 of this blog for this.
Hello Matthias,
This really well explained and I’m already waiting for next parts 🙂
I only have one thing I’m trying to figure out, do you know how to use a fixed version of UI5? I put a fixed version in index.html and deploy-yaml, but UI5 version is always the latest one.
To update the ui5 version used on SAP Launchpad Site (FLP)
https://help.sap.com/viewer/ad4b9f0b14b0458cad9bd27bf435637d/Cloud/en-US/3a0e6d6b791c4c2189f6a0a424188362.html
Add to the app manifest.json file
Once the app is deployed and rendering on cFLP, you can validate it by opening developer tools, sources, expanding out your application as shown in the attached screen shot. The Fiori app is using a different version to the default version supplied by Fiori Launchpad. Remember, the Launchpad site is using the latest to render the tiles/ settings etc but your app is using its own version.
Hi all,
finally the Github repository with the samples has been published: https://github.com/SAP-samples/btp-launchpad-ui-samples
I'm sorry for the delay. Currently I am working on part 2.
Best regards
Matthias
Hi all,
I have just published Part 2 of the blog for having a shared reuse library.
Have fun reading.
Best regards
Matthias
Hi all,
Part 3 has now been published as well.
Best regards
Matthias
Hi Matthias Schmalz
thank you , very helpful blog. You use in your example static(hardcode) image. What about dynamic... I try to use the url as property for Avatar for example:
"imageUrl":"master/ProductImages('HT-1000')/image
we don’t need extra route because it the same as for OData
manifest.json:
xs-app.json:
Hi Oleksii,
even for dynamic URLs you have to do the same resolution as in my sample. Otherwise the URL is relative to the portal index page and not to your app.
Just call the same method with your calculated relative URL.
Best regards
Matthias
Hi Matthias Schmalz,
Thanks for the detailed explanation.
When Running Standalone , index.html is the place from where the app can be accessed .
https://<tenant subdomain>.launchpad.cfapps.<landscape host>/<destination instance guid>.<service name>.<sap.app/Id>-/index.html
But when Running in Launchpad Service the app has to be accessed in different way .
https://<tenant subdomain>.launchpad.cfapps.<landscape host>/site?siteId=<site ID>#<semantic object>-<action>
Let's say i have added javascript source inside a script tag in the index.html which enables some kind of integration with the App .
How do i achieve this when opening the app from Launchpad service ?
Thanks,
Sachin
Hi Sachin,
since you are not able to provide the script via an own index.html you only have 2 options:
https://blogs.sap.com/2020/09/17/developing-a-shell-plugin-for-sap-fiori-launchpad-on-sap-cloud-platform-cloud-foundry-environment-with-sap-business-application-studio/
Best regards
Matthias
Thanks Matthias , I will try this out .