How to make UI5 content compatible with the FLP setting "Asynchronous Module Loading"? - sapui5

The "Site Settings" page available via "Manage Site" from the launchpad now allows enabling "Asynchronous Module Loading".
But once it's enabled, some of the SAPUI5 applications or FLP plugins fail to start. The browser reports in the console:
Failed to execute '<JavaScript module>.js': Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-scr * 'unsafe-inline' data: blob:".
How is CSP related to the "Asynchronous Module Loading" setting in this case and what can we do to avoid evaluating "string as JavaScript" in UI5?

Cause
If "Asynchronous Module Loading" is activated, not only does SAP Fiori launchpad (FLP) bootstrap SAPUI5 with data-sap-ui-async="true" but serves also its HTML document with the content-security-policy (CSP) response header that contains a set of CSP directives omitting unsafe-eval in script-src. Hence, UI5 applications and FLP plugins that initiate calling eval (and thus violating the CSP) won't be processed by the browser. In the future, stricter CSP directives might apply such as the script-src omitting unsafe-inline additionally.
In the legacy UI5 code, eval is called typically due to the application synchronously fetching JS modules via deprecated APIs. For other causes, see the table below.
Resolution
UI5 has already deprecated legacy/synchronous APIs and - with the 1.96 release - largely improved the support for strict CSP. UI5 content owners should adjust their code accordingly:
❌ UI5 content violating the CSP
✅ Making the UI5 content more CSP-compliant
Application's HTML document bootstrapping SAPUI5 without data-sap-ui-async="true" or with the debug mode activated.
Ensure that the HTML document bootstraps SAPUI5 with data-sap-ui-async="true" and that no debug mode is activated unnecessarily. Review Is Your Application Ready for Asynchronous Loading?
Using inline scripts (<script>...</script>) within the application's HTML document.
Use only <script src="..." ...></script> to comply with the CSP without unsafe-inline. Define the initial component declaratively via sap/ui/core/ComponentSupport.
Using deprecated APIs and libs such as jQuery.sap.*, sap.ui.requireSync, sap.ui.commons, sap.ca.scfld, ...
Review the documented API reference to learn about newer asynchronous APIs that replace the deprecated ones. Most importantly, use only sap.ui.define when defining new modules. When requiring, use sap.ui.require.
Fetching UI5 libs and components manually but still synchronously despite using non-deprecated APIs
Review the documented API reference to learn how to enable loading such resources asynchronously. E.g. when loading a UI5 lib manually:Core.loadLibrary("that.lib",/*async:*/true);
Creating the component content such as the root view, routed views, and nested views synchronously in runtime despite having them defined declaratively.
Implement the "sap.ui.core.IAsyncContentCreation" marker interface in Component.js to implicitly create the component content asynchronously.
Component-preload.js bundling JS modules as string due to:Using the outdated standard Grunt build task. Result:"my/Component.js":'sap.ui.define([...'Global instructions before calling sap.ui.define. Result:"my/Component.js":'var appID...'
Generate the Component-preload.js bundle by leveraging UI5 Tooling with e.g. ui5 build -a --clean-dest.When defining a UI5 module, avoid global instructions but only use sap.ui.define at top-level of the JS file.Result:"my/Component.js":function(){//...
For more detailed information about the current state of CSP in UI5 and which restrictions there are, see the documentation topic Content Security Policy.
Related Q&As
How to avoid "Synchronous XMLHttpRequest on the main thread" warning in UI5?
data-sap-ui-preload vs data-sap-ui-async?
How to detect eval calls when addressing UI5 modules in runtime?

Related

UI5 parameters: data-sap-ui-xx-waitfortheme and data-sap-ui-xx-componentpreload

The UI5 HTML-bootstrapper has two parameters which I don't really understand:
data-sap-ui-xx-componentpreload
data-sap-ui-xx-waitfortheme
I've checked the official documentation and didn't get some straightforward description.
My questions:
When should I use data-sap-ui-xx-componentpreload and what are its benefits?
When should I use data-sap-ui-xx-waitfortheme and what are its benefits?
⚠️ First things first ...
xx- options are experimental. They may be removed in future UI5 versions or their behavior may change in an incompatible way.
Option sap-ui-xx-componentPreload
By default, UI5 requests the app bundle Component-preload.js automatically when creating ComponentContainer (e.g. via data-sap-ui-oninit="module:sap/ui/core/ComponentSupport").
The bundle is generated by UI5 tooling for deployment so that users finally use the optimized version of the app. Therefore, avoid shipping the standalone app with data-sap-ui-xx-componentpreload in index.html! Otherwise, users will end up using unnecessarily the unminified, unbundled developer version of the app.
Options in index.html (data-sap-ui-*) don't affect typical Fiori launchpad (FLP) apps as FLP uses its own HTML page.
Using sap-ui-xx-componentPreload makes only sense for previewing, testing, or demo scenarios where there is no Node.js environment (unable to use UI5 tooling) so that 404-errors can be avoided. SAP Web IDE, for example, used to append the option sap-ui-xx-componentPreload=off in the URL so that the preview runs without the 404-error.
Values
async or sync by default depending on the sap-ui-preload / sap-ui-async settings.
off to load Component.js instead of Component-preload.js despite having a ComponentContainer.
Option sap-ui-xx-waitForTheme
The xx-waitForTheme option helps to avoid FOUC (Flash Of Unstyled Content) and, in some cases, to reduce sync XHRs. The option tells the app to postpone certain tasks until the theme has been loaded and applied.
Values (since UI5 1.63)
init waits for the theme → executes Core's init handler (attachInit(fn)) → renders the app.
Use this if some controls try to access theme-dependent parameters via sap/ui/core/theming/Parameters.get synchronously (deprecated) too soon.
rendering (formerly true until 1.62) executes Core's init handler first → waits for the theme → initializes the rendering.
If there is no value set, Core's init and initial rendering are executed immediately without waiting for the theme → FOUC.
For more options and information, see Configuration Options and URL Parameters and its parent topics.

Bootstrapping ushell_abap for variant persistence

I have a FIORI application (lunched from launchpad) in which I am using unified shell to persist variants.
In my index.html, I first bootstrap the ushell_abap and then ui bootstrap like this
<script id="sap-ushell-bootstrap" src="https://sapui5.hana.ondemand.com/1.90.0/resources/sap/ushell_abap/bootstrap/abap.js"></script>
<script id="sap-ui-bootstrap" src="https://sapui5.hana.ondemand.com/1.90.0/resources/sap-ui-core.js"
data-sap-ui-libs="sap.ushell, sap.collaboration, sap.m, sap.ui.layout, sap.ui.ux3"
data-sap-ui-theme="sap_belize"
data-sap-ui-resourceroots='{"cvg.wallapp": "./"}'
data-sap-ui-compatVersion="edge"
data-sap-ui-async="true"
data-sap-ui-frameOptions="trusted"
data-sap-ui-bindingSyntax="complex"
data-sap-ui-oninit="module:sap/ui/core/ComponentSupport">
</script>
My app works fine, including variant persistence when running in localhost.
I now deploy the APP to my Fiori server and the bootstrapping process falls apart.
It completely fails to load my theme and associated styling (no custom styling just using the belize theme).
In the console I get the following errors:
includeStylesheet-dbg.js:77 GET https://xxxx:yyyy/sap/public/bc/themes/~client-810/~cache-Bo-MdJE9H9k-CMXXsBrbgsL9ZXQ/UI5/sap/suite/ui/commons/themes/Fiori_LP_Home_Theme/library.css net::ERR_ABORTED 404 (Not found)
One or more parameters could not be found. - sap.ui.core.theming.Parameters
As soon as I remove the bootstrapping of ushell_abap everything works (except the persistence of variants of course).
I have exhausted google searches and reading through the ushell documentation here https://sapui5.hana.ondemand.com/sdk/#/api/sap.ushell
Anyone with an idea on why bootstrapping is failing when truing to use ushell_abap ?
The main issue here was that the app was using a different version of the css libraries than the bootstrap libraries which caused errors (in this case core had new elements in css styling not available in the old css library for core).
If your APP is set up as a standalone APP as explained by Benedikt Kromer and you bootstrap to a specific sapui5 version in the index.html, you can force the app to use the same version css libraries by applying the style to the core on init of the main app.
In on init I just added:
sap.ui.getCore().applyTheme("sap_belize");
This forced the app to load css libraries from the same version I used to bootstrap.
You can see this by watching the network tab in developer tools.
I think you missed one key aspect of the launchpad.
"launched from launchpad" could mean:
It is opening a new tab - standalone -> no need to mention launchpad in the question.
Staying in the same tab - standard/desired setup.
If (1) and you try to load the launchpad theme in your 1.90.0 app. Make sure the ui5 version match exactly.
"parameters could not be found" Could indicate your Theme was create for an older UI5 version.
Possible solutions:
You could generate a theme, matching the standalone ui5 version upload it into an own BSP.
You can ignore the error, there may be some
ui-glitches.
In any case 404 errors indicate also that you didn't link the theme correct in the first place.
If (2), then your index.html is never called. UI5 is starting from the component.js. Hence all your bootstrapping there dosen't count.
In this case index.html is only the playground for local development.
For the shell, this is already in place. I'm not sure why you want to load it again.

Bootstraping OpenUI5 from single JS file

OpenUI5 documentation suggests starting work by using a library loaded from CDN:
<script id="sap-ui-bootstrap"
type="text/javascript"
src="https://openui5.hana.ondemand.com/1.42.6/resources/sap-ui-core.js"
data-sap-ui-theme="sap_belize"
data-sap-ui-libs="sap.m,sap.ui.table"></script>
Unfortunately, this approach means load cascading 4 scripts on startup:
sap-ui-core.js
sap/ui/core/library.js (why ?)
sap/m/library.js
sap/ui/table/library.js
Is there way to bundle this four libraries into one script file?
Unfortunately, this approach means load cascading multiple scripts on startup:
The cascading behavior is mostly due to missing an option that tells the framework to load UI5-libraries and other modules asynchronously. In order to fix it, please add the following attribute too:
data-sap-ui-async="true" // available since 1.58.2 --> Replaces preload="async" *
data-sap-ui-preload="async" // for 1.58.1 and below
* Prerequisite: Is Your Application Ready for Asynchronous Loading?
Is there way to bundle these four libraries into one script file?
Yes; with a self-contained build (e.g. ui5 build self-contained --all ), you can reduce the size of your application as well as number of requests by bundling the required modules into a single file sap-ui-custom.js
In the above screenshot, for example, sap-ui-custom.js contains only the required modules from sap.ui.core-, sap.m-, sap.ui.table-, and sap.ui.unified-library, in addition to application related resources such as the controllers, views, etc..
See openui5-sample-app and the UI5 tooling for official documentation.

Aurelia - How to do composite applications that can be loaded at runtime

What I'm trying to do in Aurelia, is something like Prism is doing in WPF- Composite applications.
So lets say I have a "shell" application that defines the main application layout, then i have modules that I can plugin at run-time. Those modules can be an Aurelia application per se or Aurelia plugin (don't know what to use - need recommendation).
When loaded, the module needs to add it's menu items to the main application menu to expose it's features.
This is a mockup of the application:
Each module can have multiple menu items and can be pretty complex.
I'm using latest Typescript, Aurelia-CLI to create the application, and I'm using the built-in bundler : Aurelia's new built-in bundler.
So What I don't know is:
Those modules/features - what must they be? (Maybe Aurelia Plugins, or another Aurelia application?)
How to load those modules/features at run-time? (like deploy it in some plugins folder and tell the main shell application to load them)
How to modify the main menu and add new menu items from the loaded module?
Please help
Aurelia supports ultra dynamic applications. Also, there have been other community members who have had similar requirements and was able to resolve it. So I think the scenario is possible.
It seems the sub-application can just be a route.How/where to load the route should be determined based on the application URL
Those modules doesn't need to do anything specific, they can just be a normal, plain JS/TS class with lifecycle methods to handle activation/deactivation. I guess that main shell and all sub-applications need to share a common URL, you cannot have more than one router.
There could be a singleton/central store for new route to register information about loaded features, or it can be loaded upfront by a configuration file/metadata file or a database fetch.
Here is a similar question from another community member that I think can help you see how to glue things to https://discourse.aurelia.io/t/dynamicaly-load-routes/1906

How does jQuery.sap.require work?

Does SAPUI5 load the libraries each time I call jQuery.sap.require("someLibrary")?
For instance if I am calling the above statement in multiple modules in my application, is "someLibrary" loaded multiple times also?
In case someone still considers to use jQuery.sap.require, be aware that it sends only synchronous XHRs which should be avoided.
The use of jQuery.sap.require is synchronous and considered as "bad practice" because syncXHR is deprecated by the Web Hypertext Application Technology Working Group. (Source: Modules and Dependencies)
Instead, the current best practice is to use sap.ui.define or .require for asynchronous module loading:
<!-- Enable asynchronous module loading in index.html (available since 1.58.2) -->
<script id="sap-ui-bootstrap" src="..." data-sap-ui-async="true" ...>
sap.ui.define([ // or .require
// modules to load
], function(/* modules available once loaded */) {
// ...
});
Source: Asynchronify Your App by Arnd vom Hofe. Note: anonymous sap.ui.define can be called only once at top-level.
Same as jQuery.sap.require, the API sap.ui.require([/*...*/]) fetches the modules also only once depending on their internal states.
For more information, please take a look at the topics under Modules and Dependencies.
Sync XHR is deprecated not only by the web platform generally, but will be also replaced by UI5 gradually with a new set of APIs.
Source: UI5 Evolution by Peter Muessig
Consequently, jQuery.sap.require and jQuery.sap.declare are now deprecated and will be removed in the next major UI5 version (aka. "Legacy-free UI5")!
The lib is only loaded once. You can find this information in the SDK
https://sapui5.hana.ondemand.com/sdk/#docs/guide/ModularizationConcept.html
Module Loading
As mentioned already, modules are loaded by calling function jQuery.sap.require with the name of a required module. The framework then checks whether the named module is loaded already. If so, the function simply returns. Otherwise it tries to load and execute the module synchronously. If any of these two steps fails, an exception is thrown and execution of the calling module thereby is disrupted.
When you call this function with some library, it checks that given library is loaded or not using associative array. If the library is loaded, then it returns null. And if the library is not loaded, then it loads the library using sjax call and after a success of the sjax call, it sets the library name as key into associative array.
The libraries are loaded once. This can be seen in the network tab in chrome developer tools.
Also check the documentation as pointed by cevou here:
https://openui5.hana.ondemand.com/#/topic/91f23a736f4d1014b6dd926db0e91070