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
Related
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?
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.
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.
I am a newbie in angular2 and want to know the purpose of following imports we do in our angular2 app.
<script src="node_modules/es6-shim/es6-shim.min.js"></script>
<script src="node_modules/systemjs/dist/system-polyfills.js"></script>
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
<script src="node_modules/angular2/bundles/http.dev.js"></script>
so my questions regarding these imports are:
what are these imports for?
does the sequence of these imports metter?
are there any other useful imports that we must be aware of?
Thanks in advance.
I don't see any imports in your code sample. Its just including the files of various libraries into web page. If you read some of them one by one:
es6-shim.min.js - adds ES6 language features to browsers (full list)
Rx.js adds Observables library (GitHub RxJS)
http.dev.js - adds angular2 $http module to make requests
So you can google each of the names and figure out for yourself if that's applicable for your project.
More on modules and actual imports in ES2015/ES6 you can read here https://ponyfoo.com/articles/es6-modules-in-depth for example.
"useful imports that we must be aware of?"
Totally depends on your needs. By I assume angular2.dev.js and http.dev.js would be a good to start.
"does the sequence of these imports matter?"
Not now, but only if you're using proper module system so that browser can load missing parts before executing the blocks that depend on them. And also the loader file (system.js/require.js) should be in the first before all the other files.
Here are the details about what you specified in your script elements:
The two first files aren't necessary if you use TypeScript and have configuted ES5 output.
angular2-polyfills.js - Contains ZoneJS (to trigger change detection - see What is the Angular2 equivalent to an AngularJS $watch?) and reflect-metadata (to set metadata on objects from decorators)
system.src.js - SystemJS, the library that manages modules. In short when you use import and export in TypeScript the corresponding compiled code relies on it.
Rx.js - RxJS, a library that provides an implementation of Reactive Programming concepts. In short, it provides an implementArion of observables and their operators. EventEmitters, HTTP rely on this.
angular2.dev.js - Angular2 core bundle
http.dev.js - HTTP support of Angular2
Setup / Background
We're trying to load the Facebook SDK through RequireJS:
require.config({
'shim': {
'facebook' : {
'exports': 'FB'
}
},
'paths': {
'facebook': '//connect.facebook.net/en_US/all/vb'
}
});
(/en_US/all/vb.js is basically identical to the usual /en_US/all.js, but also includes FB's experimental Music Bridge feature. However, the problem still exists with either version of the SDK.)
... and the main body of the SDK is loading correctly. However, during initialisation of the Facebook SDK it creates an iframe which makes a request out to:
https://www.facebook.com/connect/ping?client_id=<CLIENT_ID>&domain=<DEVELOPMENT_SERVER>&origin=1&redirect_uri=<...>
... which then redirects to:
http://static.ak.facebook.com/connect/xd_arbiter.php?version=27#
... which in turn returns a minimal HTML document with a script tag that defines a number of additional javascript modules for the Facebook SDK.
The problem
Both the main Facebook SDK vb.js and the xd_arbiter.php script seem to make use of a require() function (defined in each file in their default top-level scope) to load Facebook SDK modules.
While the main Facebook SDK (vb.js) is loaded through a RequireJS shim (and hence - I believe - its scope is limited by RequireJS, so the require() function it defines doesn't interfere with RequireJS's global require() function), because the xd_arbiter.php code is loaded in an iframe it's somehow being executed in the global scope of the browser.
This seems to be causing a conflict with the RequireJS version of the function - every time the page loads we get a RequireJS error in xd_arbiter.php (line 13):
Error: Invalid require call
http://requirejs.org/docs/errors.html#requireargs
... which seems to be caused by the xd_arbiter.php code calling what it thinks is its own require() function, but is actually the RequireJS version of the function... and hence the parameters passed to the function are invalid for RequireJS.
We're currently in the middle of trying to replace an old legacy/custom/hand-rolled module system with a nice, common, relatively-standard library like RequireJS, and this has brought us to a screeching halt.
The question
Can anyone suggest a way to either:
Persuade xd_arbiter.php to define a require() function not in the global scope,
In addition to vb.js, also load the scripts defined in xd_arbiter.php through RequireJS instead of via an iframe, or
Persuade RequireJS to not define a global require() function (for legacy reasons we're actually using a thin wrapper around RequireJS's require() function so our existing module code keeps working for now, so it's no great imposition to change the name of the method we call to define RequireJS modules, as it's only used in one place in the code)
... without doing something gross like hacking/forking RequireJS or the Facebook SDK?
Alternatively, have we missed something really obvious somewhere that we should be doing differently? I'm pretty new to both RequireJS and the Facebook SDK, so I'm fully aware we may have just overlooked something/made a stupid mistake somewhere.
Never mind - it turned out to be a bug in the function we were (for legacy, compatibility reasons) using to wrap calls to RequireJS's require() function, that was legitimately calling require() with incorrect parameters.
For some reason some quirk of our setup was causing Firefox to report any and all JS errors on the page as having occurred in xd_arbiter.php (on a line which - by sheer dumb fluke - happened to have a call to the FB SDK's own require function!), instead of whichever actual file the error occurred in.