SAP / openui5

OpenUI5 lets you build enterprise-ready web applications, responsive to all devices, running on almost any browser of your choice.
http://openui5.org
Apache License 2.0
2.94k stars 1.23k forks source link

Loading components on demand with sap.ui.define #2203

Closed Icydillic closed 5 years ago

Icydillic commented 5 years ago

OpenUI5 version: 1.54.4

I have problems to understand this error message from the ui5loader.js

https://github.com/SAP/openui5/blob/8c09ecea323ea8dbc75e919829ef9e0b3ea38c3a/src/sap.ui.core/src/ui5loader.js#L1521-L1525

We encounter this problem within the following setup (unfortunately I don't have a running example)

Our custom component looks like this

sap.ui.define([
    "sap/ui/core/UIComponent"
], function (UIComponent) {
    "use strict";

    var CustomComponent = UIComponent.extend("sap.ui.demo.custom.Component", {
        metadata: { ... }
    });

    CustomComponent.prototype.getSomeClient = function (sUrl) {
        return new Promise (function(resolve, reject) {
            sap.ui.define(["path/to/my/component/someClient"], function (someClient) {
                resolve(someClient);
            });
        }.bind(this));
    };

    return CustomComponent;
});

And we inherit from it within another component that tries to use the getSomeClient function like this

sap.ui.define([
    "sap/ui/demo/custom/Component"
], function (CustomComponent) {
    "use strict";

    var SomeModule = CustomComponent.extend("sap.ui.demo.some.Module", {
        metadata: { ... },
        doSomething = function() {
            this.getSomeClient.then(function(client) {
                // some operations on our custom client
            }
        }
    }

Is it generally a good idea to load components "on demand" using sap.ui.define. Or is this a special case because of the inheritance? Sometimes I don't want to load all required components in a global scope. So is this still supported and best practice?

Any help is appreciated.

codeworrior commented 5 years ago

Using an unnamed sap.ui.define call nested inside another function is a ~severe~ misunderstanding of the API.

Either use unnamed sap.ui.define calls top-level in a module (ideally with no code before or after). Then the loader will assign the name according to the request (dependency from other module or sap.ui.require call).

Or use a named sap.ui.define call (first parameter is a string naming the module). But be aware that standard AMD (like requireJS) will only execute the first definition for a module ID. Any later calls will be ignored. For compatibility reasons with older versions and especially with the sync loading, sap.ui.defineunfortunately has to execute all calls, but relying on this is definitely not recommended.

But ...

...regarding your concrete scenario, I'm quite sure that you didn't even want to define a module in the getSomeClient function, you just wanted to load some external module lazily.

Just use sap.ui.require, not sap.ui.define and your problem should be gone.

[Updated] I shouldn't have written 'severe'. It sounds so unfriendly :-)

boghyon commented 5 years ago

@Icydillic According to the API reference of sap.ui.define:

A single file must not contain multiple calls to sap.ui.define. Multiple calls currently are only supported in the so called 'preload' files that the UI5 merge tooling produces. The exact details of how this works might be changed in future implementations and are not part of the API contract.

Therefore, if some modules are to be loaded on demand within a module you define, the API sap.ui.require should be used instead of sap.ui.define.

sap.ui.define([
    "sap/ui/core/UIComponent"
], function(UIComponent) {
  // ...

  CustomComponent.prototype.getSomeClient = function(sUrl) {
    return new Promise(function(resolve, reject) {
      sap.ui.require(["path/to/my/component/someClient"], function(someClient) {
        resolve(someClient);
      });
    }.bind(this));
  };
});

You can read more about this in Modules and Dependencies: Static and Dynamic Dependencies.

Icydillic commented 5 years ago

Thank you very much for enlightening my understanding in this. So if I want to use other components a sap.ui.require is a good way to go for but using a sap.ui.define will only work well on top level because it solves the dependencies with it's own sap.ui.require calls.

I'll give it a try.