abap2UI5 apps inside any UI5 app, with the npm package
@abap2ui5/embed-control
(developed in
abap2UI5/embed-control): the
examples, ready to read, run and install. A z2ui5.embed.Container control runs an abap2UI5
app - an ABAP class implementing z2ui5_if_app - in its own backend
session, wherever the host app places it:
<mvc:View xmlns:mvc="sap.ui.core.mvc" xmlns:z2ui5="z2ui5.embed">
<z2ui5:Container app="Z2UI5_CL_UI5_APP_HI_WORLD" height="400px"/>
</mvc:View>main has the examples as one npm workspace, with their tests and the build
of the branches - Develop. The branch standard has three host
apps and a card:
| Path | |
|---|---|
freestyle/ |
a UI5 freestyle app with three containers - the package is an npm dependency like any other |
fiori-elements/ |
a Fiori elements app for OData V4, list report and object page, with the control in a custom section of the object page - the abap2UI5 app gets the key of the object on the page |
fiori-elements-v2/ |
a Fiori elements app for OData V2 with the control in an extension of its object page, and in abap/ the RAP service it reads and the abap2UI5 app it starts - the app gets fields of the object on the page |
card/ |
a UI Integration Card for SAP Build Work Zone that runs any abap2UI5 app - the class is a card parameter, the backend a card destination |
src/ |
the freestyle app as the BSP Z2UI5_HOST, with the control from npm where ui5 build puts it - to try it on a system with a plain abapGit pull |
VERSION |
the commit of main, the version of @abap2ui5/embed-control and the abap2UI5 tools the branch is built from |
The branch rap is the OData V2 app on a system - one abapGit pull for a
system with RAP:
| Path | |
|---|---|
src/01/ |
the RAP service of fiori-elements-v2/abap - CDS view, metadata extension, service definition, OData V2 binding - and the abap2UI5 app Z2UI5_CL_EMBED_COUNTRY |
src/02/ |
fiori-elements-v2 as the BSP Z2UI5_HOST_FE, on that service and starting that app |
VERSION |
as on standard |
None of them carries a copy of the abap2UI5 frontend: the control loads it from the abap2UI5 installation it talks to, so the frontend always has the version of that backend.
Four steps - the first three are the same in every app, the fourth places the control.
1. Install it -
freestyle/package.json:
npm install @abap2ui5/embed-control2. Take it into the build -
freestyle/ui5.yaml.
ui5 serve serves the control anyway; ui5 build copies it into
dist/thirdparty/z2ui5/embed/ only for a dependency named here:
builder:
settings:
includeDependency:
- "@abap2ui5/embed-control"3. Register its namespace -
freestyle/webapp/manifest.json:
"sap.ui5": {
"resourceRoots": { "z2ui5.embed": "./thirdparty/z2ui5/embed/" }
}thirdparty/, not resources/: an app deployed to an ABAP system answers
every <app>/resources/ path from the UI5 of the system.
4. Place the control - in a view of your own,
freestyle/webapp/view/Main.view.xml:
<mvc:View xmlns:mvc="sap.ui.core.mvc" xmlns:z2ui5="z2ui5.embed">
<z2ui5:Container app="Z2UI5_CL_UI5_APP_HI_WORLD" height="400px"/>
</mvc:View>or, in a Fiori elements app, in a custom section - below - or in an object page extension for OData V2 - further below - or in a card - at the end.
That is all a deployed app needs, as long as the page and abap2UI5 share an
origin: the app served from the same system (BSP, launchpad), or an
approuter that routes /sap/bc/z2ui5 to it. ui5 serve needs a proxy for
/sap to the system -
freestyle/ui5.yaml
shows it. ui5-middleware-simpleproxy tells the backend the dev server's
host in X-Forwarded-Host, and abap2UI5's CSRF check compares the browser's
Origin with it - nothing else is needed.
Properties, events, the backend the control needs and the UI5 versions it supports are in the package README.
The object page of a Fiori elements app takes content of its own as a
custom section - an entry in the manifest and a fragment. There the
control runs an abap2UI5 app for the object on the page:
fiori-elements/webapp/manifest.json,
in the object page's settings:
"content": {
"body": {
"sections": {
"abap2UI5": {
"template": "demo.fe.ext.Abap2UI5Section",
"title": "abap2UI5",
"position": { "placement": "After", "anchor": "General" }
}
}
}
}fiori-elements/webapp/ext/Abap2UI5Section.fragment.xml,
bound to the object - ID is the key of the customer on the page, and
ext/Abap2UI5Section.js
turns it into the class to run and its parameters:
<z2ui5:Container
core:require="{ Section: 'demo/fe/ext/Abap2UI5Section' }"
app="{ path: 'ID', formatter: 'Section.app' }"
params="{ path: 'ID', targetType: 'any', formatter: 'Section.params' }"
height="420px"/>The ABAP class reads the key with
client->get( )-t_comp_params - a snippet, and why targetType: 'any' is
there, are in
fiori-elements/README.md.
Another customer ends the running abap2UI5 session and starts a new one
with its key.
A Fiori elements app routes by the URL hash, so the abap2UI5 behind it has to leave the hash to the page it is embedded in: abap2UI5 1.146.0 or later does. An older one clears the hash after every roundtrip, and the object page goes back to the list.
The templates for OData V2 take content of their own as a view extension of
the object page - again an entry in the manifest and a fragment, here a
section after the facet General:
fiori-elements-v2/webapp/manifest.json:
"sap.ui.viewExtensions": {
"sap.suite.ui.generic.template.ObjectPage.view.Details": {
"AfterFacet|Countries|General": {
"type": "XML",
"className": "sap.ui.core.Fragment",
"fragmentName": "demo.fev2.ext.Abap2UI5Section",
"sap.ui.generic.app": { "title": "abap2UI5" }
}
}
}fiori-elements-v2/webapp/ext/Abap2UI5Section.fragment.xml
hands three fields of the country on the page to the app:
<z2ui5:Container
core:require="{ Section: 'demo/fev2/ext/Abap2UI5Section' }"
app="{ parts: [ 'Country', 'Language', 'Nationality' ], formatter: 'Section.app' }"
params="{ parts: [ 'Country', 'Language', 'Nationality' ], formatter: 'Section.params' }"
height="420px"/>The service is a RAP service, and it is part of the example:
fiori-elements-v2/abap/
has the CDS view, its metadata extension with the facet, the service
definition, the OData V2 binding and the abap2UI5 app
Z2UI5_CL_EMBED_COUNTRY, which reads the fields by name and the country
again from the view. The dev server answers the service from a mock under
the binding's own paths; the branch rap has it all for a system -
below. It is the successor of
abap2UI5-addons/fiori-elements-integration,
without its controller extension and its launchpad target mapping -
fiori-elements-v2/README.md
compares the two.
The cards of SAP Build Work Zone are UI Integration Cards, and one of type
Component runs a UI5 component of its own - here one with the control in
its view. The card is generic: the class it runs is a card parameter, so an
administrator places it on a page as often as needed and configures each
one, the way an FLP tile names its class with ?app_start=.
card/webapp/manifest.json
names a destination instead of a URL, and the class as a parameter:
"sap.card": {
"type": "Component",
"configuration": {
"destinations": { "abap2UI5": { "name": "ABAP2UI5" } },
"parameters": { "app": { "value": "Z2UI5_CL_UI5_APP_HI_WORLD" } }
}
}card/webapp/Component.js
resolves the destination in onCardReady - Work Zone answers with a path of
its own origin that it proxies to the system behind the BTP destination -
and hands the class, the endpoint and every further parameter to the
control in
card/webapp/view/Card.view.xml:
<z2ui5:Container app="{embed>/app}" endpoint="{embed>/endpoint}" params="{embed>/params}" height="{embed>/height}"/>The further parameters reach the ABAP class as startup parameters,
client->get( )-t_comp_params - one class, configured per card. Work Zone
routes by the URL hash as well, so the card needs the same abap2UI5 as the
Fiori elements app: 1.146.0 or later.
card/README.md
lists the parameters and the way into Work Zone, and what to check on the
first card.
git clone --branch standard https://github.com/abap2UI5/samples-embed-control.git
cd samples-embed-control/freestyle # or fiori-elements, fiori-elements-v2, card
npm installAgainst an SAP system - npm start: set the system's URL as baseUri in
ui5.yaml, copy .env.example to .env and put user and password there.
Without an SAP system - npm run start-local: the proxy goes to
http://localhost:3000 (ui5-local.yaml), abap2UI5 transpiled to JavaScript
and run in Node: the npm package
@abap2ui5/node-runtime,
in a folder of its own, with Node 22 or later:
mkdir abap2ui5-backend && cd abap2ui5-backend
npm install @abap2ui5/node-runtime express
node --input-type=module -e 'import { serve } from "@abap2ui5/node-runtime"; await serve({ port: 3000 });'Either way, ui5 serve serves only the libraries framework.libraries
in the example's ui5.yaml names: the ones the ABAP views of the embedded
apps use, and sap.ui.codeeditor for abap2UI5's developer tools
(Ctrl+F12). A library missing there fails in the browser console with a
script load error; an app deployed to a system has every library of that
system.
The Fiori elements apps and the card need abap2UI5 1.146.0 or later. The
READMEs of
freestyle/,
fiori-elements/,
fiori-elements-v2/
and
card/
have both ways. The Fiori elements apps answer their own OData services from
mock data, and take SAPUI5 from npm - Fiori elements is not part of
OpenUI5. The card opens on a preview page: three cards and a stand-in for
Work Zone. npm run build writes an app - or the card - to deploy into
dist/.
- abap2UI5 1.145.0 or later,
with its HTTP service
/sap/bc/z2ui5active - the node thestandardbranch of abap2UI5/frontend brings, for example. - Pull the branch
standardof this repository with abapGit into a new package. It creates the BSPZ2UI5_HOSTwith the ICF nodes/sap/bc/ui5_ui5/sap/z2ui5_hostand/sap/bc/bsp/sap/z2ui5_host- nothing else, and nothing shared with theZ2UI5BSP of abap2UI5/frontend. abapGit readssrc/only;freestyle/,fiori-elements/,fiori-elements-v2/andcard/stay out of the system. - Activate the two ICF nodes in
SICF. - Open
/sap/bc/ui5_ui5/sap/z2ui5_host/index.html.
What to look for: three hello world apps. In the browser's network tab,
thirdparty/z2ui5/embed/Container.js and .css of the BSP, one
GET /sap/bc/z2ui5?z2ui5-bundle and no other file of the frontend; every
POST goes to /sap/bc/z2ui5. An abap2UI5 older than 1.145.0 answers the
bundle request with its page - the control then says so ("abap2UI5 could not
start") instead of starting.
BSP Z2UI5_HOST the host app
├─ thirdparty/z2ui5/embed/ the control, from npm
└─ z2ui5.embed.Container
├─ GET /sap/bc/z2ui5?z2ui5-bundle the frontend as a script, once per page
└─ POST /sap/bc/z2ui5 the roundtrips, one session per control
The Fiori elements app for OData V4 has no BSP: it needs its OData
service, which the example mocks and a system does not have - an app of your
own brings its RAP service. The one for OData V2 brings its RAP service, and
has its BSP on the branch rap. The card has none: a card is deployed to
its host.
The Fiori elements app for OData V2 with its RAP service, on a system with
RAP view entities and OData V2 bindings - SAP S/4HANA 2020 (ABAP 7.55) or
later, standard ABAP (the view reads T005T, which ABAP Cloud does not
release).
- abap2UI5 1.146.0 or later, with its HTTP service
/sap/bc/z2ui5active. - Pull the branch
rapof this repository with abapGit into a new package. It creates the RAP service andZ2UI5_CL_EMBED_COUNTRY(src/01) and the BSPZ2UI5_HOST_FEwith the ICF nodes/sap/bc/ui5_ui5/sap/z2ui5_host_feand/sap/bc/bsp/sap/z2ui5_host_fe(src/02) - nothing shared with the branchstandard, which installs next to it. - Publish the service binding
Z2UI5_UI_EMBED_COUNTRY_O2(ADT: open it, Publish) if the pull did not, and activate the two ICF nodes inSICF. - Open
/sap/bc/ui5_ui5/sap/z2ui5_host_fe/index.html.
What to look for: the countries of the system in the list report; on the
object page a section abap2UI5 after General Information, in it the
startup parameters as the page sent them and the country as the app read it
from Z2UI5_C_EMBED_COUNTRY. Another country starts the app anew, and the
URL stays on the object page.
BSP Z2UI5_HOST_FE the Fiori elements app
├─ GET /sap/opu/odata/sap/Z2UI5_UI_EMBED_COUNTRY_O2/ the RAP service
└─ z2ui5.embed.Container in the object page
├─ GET /sap/bc/z2ui5?z2ui5-bundle the frontend, once
└─ POST /sap/bc/z2ui5 Z2UI5_CL_EMBED_COUNTRY
- One frontend per page: the first control that starts decides where it comes from; every control still sends its roundtrips to its own endpoint.
- Embedding is still page-wide in places - busy indicator, title, the
sap.m.Approot - until abap2UI5's embedded mode covers them (backlog item); the URL hash is the host's from abap2UI5 1.146.0 on. - Custom controls from
Z2UI5_CCI/Z2UI5_CCC: the bundle hands over their paths like abap2UI5's own page does; not tried yet. - The card is tried against a stand-in for Work Zone, not yet on a Work Zone
tenant: abap2UI5's Origin check behind the destination proxy is the first
thing to look at ("What to check on the first card" in
card/README.md). - The host app's own
Component-preload.jsdoes not exist in a BSP pulled with abapGit (a page name may not contain a hyphen): UI5 answers the 404 by loading the host's few files one by one. An app deployed from itsui5 buildoutput has it.
main is the source of both branches: freestyle/, fiori-elements/,
fiori-elements-v2/ and card/ are the examples, one npm workspace that
installs the control from npm once, for all of them, in the version
package-lock.json names.
npm ci
npm run start-local # the freestyle example - start, start:fe, start:fe-v2, start:card for the others| Command | |
|---|---|
npm run lint / npm run format:check |
ESLint and Prettier |
npm run abaplint |
the ABAP of fiori-elements-v2/abap against abap2UI5's main (abaplint.jsonc) |
npm run build |
ui5 build of the examples - the control lands in dist/thirdparty/z2ui5/embed/ |
ABAP2UI5_DIR=../abap2UI5 npm run bsp |
the branches in out/standard/ and out/rap/ (scripts/build-bsp.mjs), built with abap2UI5's BSP tools and checked with its page invariants |
npx playwright test |
the examples in a browser against the backend on port 3000 - the freestyle one on UI5 1.136 and 1.71, the Fiori elements ones on SAPUI5 1.136, the card on OpenUI5 1.136 (PW_CHROMIUM_PATH for an installed Chromium) |
CI (ci.yaml) runs all of them on every pull request and every night, the
e2e tests against abap2UI5 main and against 1.145.0, the backend floor of
the control. On every push to main, deliver.yaml runs the same CI,
builds the branches and writes each as one commit on top of main - never
change a branch by hand. A new version of the control arrives as a pull
request that bumps it in package-lock.json (dependabot, weekly, or by
hand after a release).
abap2UI5/embed-control runs
the branch build and the e2e tests of main in its own CI, with the control
of its commit in place of the one from npm - so a change to the control is
tested against these examples before it is published.
| Content | Where |
|---|---|
| the examples, the RAP service, the tests, the build of the branches, this README | here, on main |
the branches standard and rap |
written by deliver.yaml from main - a hand edit is gone with the next delivery |
| the control | abap2UI5/embed-control - packages/embed-control, published to npm as @abap2ui5/embed-control |
the abap2UI5 frontend and ?z2ui5-bundle, the BSP tooling |
abap2UI5/abap2UI5 - app/webapp, z2ui5_cl_ui5_http_handler, tools/ |
For bug reports or feature requests, open an issue here (the examples), in abap2UI5/embed-control (the control) or abap2UI5/abap2UI5 (the frontend, the backend).