openui5
openui5 copied to clipboard
`sap.ui.core.IAsyncContentCreation` leads to "Cannot read properties of null (reading `getId`)" on `Fragment` loading
OpenUI5 version: 1.95
Browser/version (+device/version): Chrome DEV
I'm trying to adopt recently introduced sap.ui.core.IAsyncContentCreation
:
metadata: {
interfaces: [
"sap.ui.core.IAsyncContentCreation"
],
manifest: "json"
},
But once I've added sap.ui.core.IAsyncContentCreation
, I face an issue with loading a Fragment
:
I've checked deeper and found out, that the problem is with oView
, which is simply null
:
this._oDialog = await Fragment.load({
controller: fragmentController,
id: oView.getId(),
name: "webapp.view.AuthDialog"
});
The place where this._oView
is initialized:
constructor: function constructor(oView) {
this._oView = oView;
}
I assume, that the problem is that the system has not enough time to load and initialize oView.getId()
due to async nature introduced by sap.ui.core.IAsyncContentCreation
. I've tried to force waiting for the oView
with async
/await
, but it didn't work. Once I remove sap.ui.core.IAsyncContentCreation
, everything is working correctly but in a sync way.
My question is:
How to tell UI5 to wait until the Fragment
is loaded if using sap.ui.core.IAsyncContentCreation
?
And my questions are:
- where do you create the fragment, in what hook / at what time?
- and how do you get access to
oView
? Is it the result of a factory or a reference taken from a controller?
The error says that oView
is null, not the ID.
@codeworrior, thanks for your prompt response!
* where do you create the fragment, in what hook / at what time?
I create the authDialog
fragment at Component.js's init()
:
init(...args) {
const oDeviceModel = new JSONModel(Device);
oDeviceModel.setDefaultBindingMode(BindingMode.OneWay);
this.setModel(oDeviceModel, "device");
UIComponent.prototype.init.apply(this, args);
this.getRouter().initialize();
this._authDialog = new AuthDialog(this.getRootControl(0));
},
AuthDialog
is defined in sap.ui.define(["./controller/AuthDialog")
and the AuthDialog
constructor receives the oView
, which is null
:
constructor: function constructor(oView) {
this._oView = oView;
},
Then, in BaseController.js, at the async onBeforeRendering()
I run this fragment: this.getOwnerComponent().openAuthDialog();
.
The BaseController.js is called from the another view:
(BaseController) => BaseController.extend("webapp.controller.loginBackground", {}));
* and how do you get access to oView? Is it the result of a factory or a reference taken from a controller?
In a BaseController.js I call for this.getOwnerComponent().openAuthDialog();
, which calls this._authDialog.open();
.
this._authDialog
is defined in init(...args) {…}
of BaseController.js, please, see the code snippet above.
Hopefully it was not so complicated explanation.
The error says that oView is null, not the ID.
Right, I've updated the initial post.
To conclude, the problem is that this.getRootControl(0)
is null
due to sap.ui.core.IAsyncContentCreation
.
If I remove sap.ui.core.IAsyncContentCreation
, then this.getRootControl(0)
is not null
anymore and everything is OK.
Exactly.
By implementing sap.ui.core.IAsyncContentCreation, your component claims that it is prepared to handle framework-controlled content creation in an async manner. As discussed in #3112 with @Thodd, this includes the root view. As the root view is created fully async (by the default implementation of createContent
, called from within UIComponent#init
), it won't be available in the same browser task in which the init
call happened.
You could use rootControlLoaded
before creating the fragment. Or you consider to make the dialog creation part of the root view's controller code (to me, the ID-coupling of view and authDialog suggests that the root view's controller could do the creation). But then you would have to use rootControlLoaded
before accessing the fragment or you would have to ensure that the fragment is only accessed after the component creation finished (not sure if this is the case in your scenario).
I see that the documentation of getRootControl
needs to be adapted to reflect the behavior when async content creation is used.
BTW: getRootControl
does not expect any parameters.
Thanks, I'll try to figure out how to implement such async behavior. But up-to-dated documentation with real-life code snippets would be welcomed.
Regarding getRootControl
, which does not expect any parameters.
I've added 0
as an input parameter according to the IDEA documentation:
deprecated
UIArea.getRootControl(idx: int): Control
(since 1.1) - use function#getContent
instead Returns the content control of thisUIArea
at the specified index. If no index is given the first content control is returned. openui5getRootControl(): Control
Is it a correct info and #getContent
should be used instead of deprecated #getRootControl
?
No, UIArea
and UIComponent
are two different things and there's no getContent
for the UIComponent
.
Maybe IDEA only recognizes the method name getRootControl
and mistakenly shows the documentation for UIArea
?
Maybe IDEA only recognizes the method name
getRootControl
and mistakenly shows the documentation forUIArea
?
IDEA relies on the https://github.com/DefinitelyTyped/types/openui5 project, there you can find:
/**
* @deprecated (since 1.1) - use function {@link #getContent} instead
*
* Returns the content control of this `UIArea` at the specified index. If no index is given the first content
* control is returned.
*/
getRootControl(
/**
* index of the control in the content of this `UIArea`
*/
idx: int
): Control;
I don't know if it's a right or a wrong resolving, but UIComponent
comes from class sap.ui.core.UIComponent
.
BTW, is it a right assumption that with sap.ui.core.IAsyncContentCreation
implemented there is no more need in "async": true
in routing/config
and rootView
of manifest.json?
You could use
rootControlLoaded
before creating the fragment.
@codeworrior, thanks a lot, it looks like I solved the issue by turning init(…)
into async
function and awaiting for the rootControlLoaded
:
async init(...args) {
const oDeviceModel = new JSONModel(Device);
oDeviceModel.setDefaultBindingMode(BindingMode.OneWay);
this.setModel(oDeviceModel, "device");
UIComponent.prototype.init.apply(this, args);
this.getRouter().initialize();
await this.rootControlLoaded();
this._authDialog = new AuthDialog(this.getRootControl());
},
I'm not sure if it's the best possible solution but now my fragment displayed correctly.
Two questions:
- Is calling
await this.rootControlLoaded();
what you meant or you actually proposed something else? - Although I'm using
sap.ui.core.IAsyncContentCreation
,rootView/"async": true
withrouting/config/"async": true
in manifest.json, anddata-sap-ui-async = "true"
in HTML-bootstrapper, I still get:
[Deprecation] Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/. warning in DevTools
My view has only one authDialog
fragment and the bootstrap configuration in HTML is:
<script data-sap-ui-async = "true"
data-sap-ui-compatVersion = "edge"
data-sap-ui-excludejquerycompat = "true"
data-sap-ui-frameOptions = "deny"
data-sap-ui-onInit = "module:sap/ui/core/ComponentSupport"
data-sap-ui-resourceroots = '{
"webapp": "./"
}'
data-sap-ui-theme = "sap_fiori_3"
data-sap-ui-xx-componentpreload = "off"
data-sap-ui-xx-nosync = "true"
id = "sap-ui-bootstrap"
src = "https://openui5nightly.hana.ondemand.com/resources/sap-ui-core.js">
</script>
So, what could be a reason for «Synchronous XMLHttpRequest on the main thread» warning?
Regarding:
Or you consider to make the dialog creation part of the root view's controller code (to me, the ID-coupling of view and
authDialog
suggests that the root view's controller could do the creation).
The whole idea to be able reusing and showing this dialog on any view, that's why to achieve the maximal degree of reuse, I followed the Walkthrough tutorial recommendation and decoupled this fragment and it's controller from any view's controller, including the root view. I'm not sure if's the best practice, but i remember that the Walkthrough tutorial advised doing so.
Again, @codeworrior, thanks a lot for the providing explanation and possible ways to solve the issue!
I think one possible reason for the remaining syncXHR might be the loading of CLDR data. Or do you see any *.js XHRs in the network tab (there should be none, we use them only in the sync loading case)?
Reg. async init
: please be aware that the framework will not wait for the promise returned by your init
method.
The purpose of IAsyncContentCreation
rather is to allow createContent
to become async.
@pubmikeb About the remaining sync XHR calls, here I documented what I'm currently aware of: https://blogs.sap.com/2018/04/26/ui5ers-buzz-29-asynchronify-your-app/comment-page-1/#comment-545788
The CLDR is one of them.
Or do you see any *.js XHRs in the network tab (there should be none, we use them only in the sync loading case)?
Since it's a sandbox project for testing modern UI5 features, the entire Network tab content is:
I don't see any *.js XHRs.
Regarding «syncXHR might be the loading of CLDR data», is there any way to identify if it's a case and then to get rid of these sync requests?
Reg.
async init
: please be aware that the framework will not wait for the promise returned by yourinit
method. The purpose ofIAsyncContentCreation
rather is to allowcreateContent
to become async.
Without async init
I can't await for this.rootControlLoaded();
. So, does it mean that my solution will work only occasionally and can't guarantee the stable work?
https://blogs.sap.com/2018/04/26/ui5ers-buzz-29-asynchronify-your-app/comment-page-1/#comment-545788
@boghyon , among the described by you reasons, I had only "preload": true
for the i18n
model, I've removed it but still have the warning.
@pubmikeb The issue https://github.com/SAP/openui5/issues/3134 refers rather to the v2.ODataModel
requiring sap/ui/thirdparty/datajs.js
synchronously if "preload": true
. Not sure about the current state of i18n resources.
BTW, some lifehack how to see who is leading to sync requests is to use a Permissions-Policy feature aka Feature-Policy:
res.setHeader("permissions-policy", "sync-xhr=()");
then you'll get in DevTools a detailed report about every case of sync. request:
@pubmikeb The issue #3134 refers rather to the
v2.ODataModel
requiringsap/ui/thirdparty/datajs.js
synchronously if"preload": true
. Not sure about the current state of i18n resources.
There is also a known issue wrt. sync loading of i18n files.
This can happen if you use {{placeholder}}
texts in the manifest.json, e.g. for "sap.app"/description
.
Additionally, we have seen that this issue also occurs if you have multiple Components extending each other.
So each parent Component might cause an additional sync XHR for the i18n file.
Specifically you should see that the same i18n files is loaded twice, once async and once sync :(
Though it's hard to say if this is the case here, but judging from @pubmikeb's last screenshot, I think it is. This is a problem which we might tackle in the future, so for now I don't think there is anything you can do.
Guess I forgot to mention this earlier, the i18n issue I described is already tracked internally via a backlog item: CPOUI5FRAMEWORK-286
I can't say when this will be tackled, but in a productive (bundled) scenario, the issue should not be visible. So at the moment it's not planned in the near future.
PS: Let's keep this open until the issue is solved.
@pubmikeb: can you please look into your manifest file to see if you use {{placeholder}}
strings?
Also if possible can you isolate the issue in a jsbin?
It seems that the discussion ended as there was no answer from the author for more than a month. Thanks to Thodd for detailed information on the topic. I'm closing the ticket.
I forgot to mention this earlier, the i18n issue I described is already tracked internally via a backlog item: CPOUI5FRAMEWORK-286
That's a great news.
Let's keep this open until the issue is solved.
@thodd, I agree!
Can you please look into your manifest file to see if you use
{{placeholder}}
strings?
Yes, I have {{placeholder}}
, e.g.:
"sap.app": {
"_version": "1.16.0",
"applicationVersion": {
"version": "1.0.0"
},
"description": "{{APP_DESCRIPTION}}",
}
Also if possible can you isolate the issue in a jsbin?
I need to repro this case in my code-sandbox and in case I can repro, I'll try to share the sample project.
@Shtilianova, please, consider re-opening this issue.
I've took a Shop Administration Tool as a sample app for the base, and implemented sap.ui.core.IAsyncContentCreation
in Component.js
. Now I don't experience xhr
/sync
requests in case of i18n
.
I'll have a look into the Shop Admin app, but I would have expected to see a sync XHR for already asynchronously loaded i18n files. Still, it's present without the Interface anyway, which is bad in itself...
Edit: Yep I can reproduce the problem with the linked Shop Admin Tool, this app also already implements the sap.ui.core.IAsyncContentCreation
interface.
At least one i18n file is loaded first async, and then sync again.
@pubmikeb: Any idea why you did not see the sync XHRs?
@pubmikeb: Any idea why you did not see the sync XHRs?
@Thodd, sorry for the very long delay with the response. I've rechecked the Shop Administration Tool, and I do observe the XHR-requests.
Under
Now I don't experience
xhr
/sync
requests in case ofi18n
.
I meant that my own playground app, based on the Shop Administration Tool, doesn't have XHRs anymore. But in my test app I've tried to follow the latest async guidelines, including the modern manifest configuration.
@pubmikeb: Thanks for rechecking this, so at least it's consistently wrong ;)
Guess I forgot to mention this earlier, the i18n issue I described is already tracked internally via a backlog item:
CPOUI5FRAMEWORK-286
@Thodd, I'm just curious, if is there any update on the subject?
Hi @pubmikeb,
I recently stumbled upon this again, and yes there is indeed an update now. We are currently fixing another issue wrt. synchronous ResourceBundle loading.
In the wake of UI5 2.0 we will have to address this too. Not sure when though... still, the issue came up again and needs to be fixed before the 2.0 release.
BR, T
needs to be fixed before the 2.0 release
Such a great news!
Hi @pubmikeb,
the sync loading of the i18n file of a component is now replaced by an asynchronous request with change ad5cf65. This will be available with UI5 version 1.121 and upwards.
Best regards, Jiawei
the sync loading of the i18n file of a component is now replaced by an asynchronous request
@stopcoder, are any changes required to the end user UI5-app codebase?