Homelab, Linux, JS & ABAP (~˘▾˘)~
 

[SAPUI5] Promisify an oData request

This is discussed for many years and unfortunately will not be implemented in the UI5 framework itself (see here). There are already different blogs describing how to build a wrapper for oData requests (for example here and here).

But with ES2024 it now got super simple to do this:

async function readData(model, entitySet) {
  const [promise, resolve, reject] = Promise.withResolver( )
  model.read(entitySet, {
    success: data => resolve(data),
    error: error => reject(error)
  })
  return promise
}

const user = await readData(oDataModel, "/user")

[SAPUI5] Model binding events

this.getView().bindElement({
				path: sObjectPath,
				events: {
					dataRequested: (oEvent) => {}, // Executed when a request to server is send
					dataReceived: (oEvent) => {},  // Executed when data from server is received
					change:(oEvent) => {},         // Executed everytime you do ElementBinding
				}
			})

The events for dataRequested and dataReceived are only fired, when data is requested or data is received from a backend. This is not the case, when the requested data is already available in the model from a previous backend call. In such situations, the change event comes in handy.

The same can also be done via XML:

binding="{
  path: '/myEntitySet',
  events: {
    dataRequested: 'onDataRequested',
    dataReceived: 'onDataReceived',
    change: 'onDataChange'
  }
}"

[SAPUI5] securityTokenAvailable

Just noticed, that with UI5 version 1.119.0 the getSecurityToken() function got replaced with securityTokenAvailable().

https://sapui5.hana.ondemand.com/#/api/sap.ui.model.odata.v2.ODataModel%23methods/getSecurityToken

https://sapui5.hana.ondemand.com/#/api/sap.ui.model.odata.v2.ODataModel%23methods/securityTokenAvailable

// Returns the current security token if available; triggers a request to fetch the security token if it is not available.
const token = this.getModel().getSecurityToken() // Deprecated

// Returns a promise, which will resolve with the security token as soon as it is available.
const token = await this.getModel().securityTokenAvailable()

[SAPUI5] Add your own Logout functionality to the Launchpad Sandbox

Use the attachLogoutEvent of the ushell container to trigger your approuter logout endpoint, that needs to be configured in your xs-app.json. The code in my launchpad.html looks like this:

    <script>
        sap.ui.getCore().attachInit(() => {

            sap.ushell.Container.createRenderer('fiori2', true).then(renderer => renderer.placeAt("content"))

            sap.ushell.Container.attachLogoutEvent(e => {
                e.preventDefault()
                window.location.replace('/do/logout')
            }, false)

        })
    </script>

This way, you can reuse the default logout dialog logic the launchpad provides.

For completeness, find also my approuter configuration and custom logout page below.

My xs-app.json:

{
  "welcomeFile": "index.html",
  "authenticationMethod": "route",
  "logout": {
    "logoutEndpoint": "/do/logout",
    "logoutPage": "/logged-out.html"
  },
  "sessionTimeout": 60,
  "routes": [
    {
      "source": "^/logged-out.html$",
      "localDir": ".",
      "authenticationType": "none"
    },
    {
      "source": "^/launchpad.html$",
      "localDir": ".",
      "authenticationType": "xsuaa",
      "cacheControl": "no-cache, no-store, must-revalidate"
    },
    {
      "source": "^/appconfig/(.*)$",
      "localDir": ".",
      "authenticationType": "xsuaa"
    },
    {
      "source": "^/user-api(.*)",
      "target": "$1",
      "service": "sap-approuter-userapi",
      "authenticationType": "xsuaa"
    },
...
  ]
}

My logged-out.html file, placed in the approuter folder next to the launchpad.html:

<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Logged out</title>
    <style>
        h2 {
            font-family: "Arial", sans-serif;
        }
        .centered {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
        }
    </style>
</head>

<body>
    <div class="centered">
        <h2>You are now logged out</h2>
    </div>
</body>

</html>

[SAPUI5] Add custom header parameter to all oData queries

Simply go to your Component.js file and add this line to the init function:

this.getModel().setHeaders({"myCustomParameter": "test"})

In a CAP Backend, you get this parameter from the express req object, which can be accessed via the req.http property:

req.http.req.headers['myCustomParameter']

And here is a nice code snippet, on how to read this header parameter in an ABAP system: https://answers.sap.com/answers/425621/view.html

[SAPUI5] Suppress default oData error message / display meaningful error messages

Create a separate ErrorHandler.js file, like it is described here and either do your own error handler implementation, or take the sample from here. To avoid displaying multiple errors at once, follow this chapter.

Thanks to the provided dsag sample, this is takes only a few minutes and improves the user experience a lot!

[SAPUI5] Create date object in UTC YYYY-MM-ddTHH:mm:ss

https://sapui5.hana.ondemand.com/#/api/sap.ui.core.format.DateFormat%23methods/format

            const today = new Date()
            const oDateTimeFormat = sap.ui.core.format.DateFormat.getDateTimeInstance({
                pattern: "yyyy-MM-ddTHH:mm:ss",
                UTC: true
            })
            const todayISO = oDateTimeFormat.format(today)

The UTC flag can also be set, when calling the format function.

            const today = new Date()
            const oDateTimeFormat = sap.ui.core.format.DateFormat.getDateTimeInstance({
                pattern: "yyyy-MM-ddTHH:mm:ss",
                //UTC: true
            })
            const todayISO = oDateTimeFormat.format(today, true)