⚠️This blog post is outdated.⚠️
The technology has evolved and I have reworked this blog series. Please find the new version here: https://community.sap.com/t5/technology-blogs-by-sap/designing-ui5-apps-as-business-solution-for-sap...
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
Part 4: Splitting the Destination Service instance
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:
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.
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.
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:
The deployment of this is described in the mta.yaml of the project. It is structured as follows:
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/
- 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 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.
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.
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" }
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.
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...
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...
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.
Beware that there are other locations, where this automatic resolution does not happen.
For example this applies when you do an AJAX request via coding or specify an URL in a control property like the src of an image.
Our app contains an image control in the header area that shows a logo from a local file to demonstrate this.
Here the browser would resolve an URL relative to the document location which is the index.html. Using simple hardcoded URLs only works, as long as the app is located next to the index.html.
Why this can cause issues and how to solve this will be explained below.
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.
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...
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.
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.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
14 | |
12 | |
11 | |
10 | |
7 | |
6 | |
6 | |
6 | |
6 | |
5 |