Tobias
Tobias

Reputation: 5463

How to ensure that data for JSONModel are loaded?

I maintain an SAP Fiori app that has been running for two years without any problems. After upgrading SAPUI5 from 1.56.x to 1.101.x, there are various errors that can be traced back to a place where I try to load data for a JSON model.

The error is:

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'indexOf')

This is the code:

onAfterRendering: function (oEvent) {
  var sButtonId = this.getModel("misc").getProperty("/ButtonId");
  // ...
  // Here the code breaks because the variable is undefined:
  if (sButtonId.indexOf("Demand") > -1) { 
    //...
  }
},

The error usually only appears when navigating to the app from the SAP Fiori launchpad. If you reload the app itself, 99.9% of the time no error occurs.

There is no call that describes or reads the variable beforehand. The error appears directly at the first use.

I was trying to debug the UI5 framework a bit and came across internal method JSONModel.prototype._getObject:

Screenshot of sap.ui.model.json.JSONModel#_getObject source code

When I call the app directly, this method returns the appropriate data and the app works. When I call the app from the launchpad, the method returns null. The model has not been loaded yet (oNode === undefined).

I then looked at the network and found that when I call the app directly, the JSON file/model has already been loaded before the above function is called, whereas when I load the app from the launchpad, the JSON file has been requested but no content has been served yet.

So it seems to be an asynchronous problem. I cannot use async/await, because UI5 officially only supports ECMAScript 5 (ES5) and we cannot deploy otherwise.

The model is declared and preloaded via the app descriptor (manifest.json).

Upvotes: 2

Views: 1027

Answers (1)

Boghyon Hoffmann
Boghyon Hoffmann

Reputation: 18064

Same as a/61879725 or a/63892279, you'll need to refactor the code by using dataLoaded1 from the JSONModel to ensure that the data have been loaded:

// Given: JSONModel and data source declared in the application descriptor (manifest.json)
thatJSONModel.dataLoaded().then(function() { // Ensuring data availability instead of assuming it.
  var sButtonId = thatJSONModel.getProperty("/ButtonId"); // sButtonId is now defined.
  // ...
}.bind(this));

Btw. I'd strongly discourage applications from relying on onBeforeRendering/onAfterRendering since it's unpredictable for them when and how many times the framework or a control initiates rendering.2


1 API Reference: sap.ui.model.json.JSONModel#dataLoaded
2 For example, the recent commit 8e24738 reduces the number of render calls in applications.

Upvotes: 4

Related Questions