Skip to Content
0

Initialize and read ODataModel in Component init

Nov 17, 2017 at 02:57 PM

119

avatar image

Hello,

Here is the scenario I try to achieve:

I need to load an ODataModel from the backend before displaying the rootView.

It's a kind of user bootstrap configuration. If it fails to load, there is no use to even display the root view. I just want to display an error message(i.e: "failed to load the application configuration").

I want to be sure first that I have the metadata loaded successfully, and then that I have some JSON data from the read() call.

But as those calls are asynchronous, I don't know how to prevent the call to UIComponent.prototype.init.apply(this, arguments) if my user configuration is not loaded correctly from the backend.

This is what I tried. If the OData is not loaded correctly, it's fine, I get the error MessageBox. But if it's successfull nothing happen when executing the init.apply (root view is not displayed, and there are no errors in the console):

_initUserConfigModel and _loadUserConfig are methods which returns a promise.

Component.js

init: function() {
  var that = this;
  var originalArgs = arguments;

  var userConfigPromise = this._initUserConfigModel()
  .then(this._loadUserConfig, function(oError) {
     MessageBox.show("error");
  });

  $.when(userConfigPromise).done(function() {
    UIComponent.prototype.init.apply(this, originalArgs);
    this.setModel(models.createDeviceModel(), "device");
  }.bind(this)); 
},

_initUserConfigModel: function() {
  var that = this;
  var oDeferred = new jQuery.Deferred();
			
  var sServiceUrl = that.getManifestEntry("/sap.app/dataSources/userConfig/uri");
  var oUserConfigModel = new sap.ui.model.odata.v2.ODataModel(sServiceUrl)
	.attachMetadataLoaded(function() {
	    oDeferred.resolve(oUserConfigModel);
	})
	.attachMetadataFailed(function(oEvent) {				   
            oDeferred.reject(oEvent.getParameters());
	});
  return oDeferred.promise();
},
		
_loadUserConfig: function(oModel) {
  var oDeferred = new jQuery.Deferred();
			
  if (oModel) {
      var aFilters = [];
      // ... some code for filters
      oModel.read("/EntitySet", {
         urlParameters: {"$expand":"aaaa/bbbb"},
	 filters: aFilters,
	 success: function(oData, oResponse) {
	    var jsonModel = new JSONModel({EntitySet: oData.results});
	    oDeferred.resolve(oData.results);
	 }, 
	 error: function(oError) {				        
            oDeferred.reject(oError);
	 }
      });
  }  
  return oDeferred.promise();
}

Of course if I do the following below it works but then it's not the behavior I want because the root view is displayed in parallel of the call to the userConfiguration:

init: function() {
  var that = this;
  var originalArgs = arguments;

  var userConfigPromise = this._initUserConfigModel()
  .then(this._loadUserConfig, function(oError) {
     MessageBox.show("error");
  });

  $.when(userConfigPromise).done(function() {
    // write some logs
  }.bind(this)); 

  UIComponent.prototype.init.apply(this, originalArgs);
  this.setModel(models.createDeviceModel(), "device");
}

So what's the best way to initialize an OData model before initializing the rootView, router, etc) ?
10 |10000 characters needed characters left characters exceeded
* Please Login or Register to Answer, Follow or Comment.

4 Answers

Irfan Gokak Nov 20, 2017 at 10:04 AM
0

Hi,

Declare it in manifest.json like below.

// Inside sap.app component

"dataSources": {
			"mainService": {
				"uri": "yourodataservice",
				"type": "OData",
				"settings": {
					"odataVersion": "2.0",
					"localUri": "localService/metadata.xml"
				}
		}

// Inside models of sap.ui5 component
// below i18n model
			"": {
				"dataSource": "mainService",
				"preload": true
			},

Share
10 |10000 characters needed characters left characters exceeded
Guillaume Bouzebra Nov 21, 2017 at 02:39 PM
0

Not a solution for my scenario. I want it out of the manifest because I want control over its initialization. I want the application to not continue if it failed loading those data. I want to control the error message when the service is not reachable.

When initializing from the manifest, the framework will try to call the $metadata endpoint of the service. At that time, several issues could already happen:

- endpoint is not reachable because of an error in the URI

- endpoint is not reachable because of a network issue

- access is denied (authorization missing in the backend)

But if one of those issues happen, the error is only logged to the console, and the model remains "undefined". Then the root view process is started and when a call will be made later on, there will be an exception because the model instance is undefined.

I want control over this particular model initialization, that's why I get it out of the manifest. My other OData models are in the manifest.

So even if the "preload" ensure it's loaded before the Component, how I can control the error flow?

In the doc, it said

  • As model events may be missed because they are fired before the component coding runs, we recommend to use promises instead, depending on the model type.

But it's not very explicit on where and how to do it ....

Share
10 |10000 characters needed characters left characters exceeded
Joseph BERTHE Nov 22, 2017 at 05:04 AM
0

At the end of the document you have a code :

Before enabling the preload for the V2 ODataModel, make sure that you listen properly to metadata loaded by using the Promise API instead of the Event API (metadataLoaded) since the preload could have loaded the metadata already before the application code is executed. The Promise will be executed even if the metadata loaded event has been raised already.

Listen properly to metadata loaded by using the Promise:

oModel.metadataLoaded().then(function(){/* TODO: add the event handling here! */});

You can as weel inspire yourself with this blog where it explain how to deal with promise and Metadataloarded in the compoment.js. I will suggest you to replace the code where (in the blog) it created an OData model by something like this :

oModel = this.getModel();
this._createMetadataPromise(oModel);

Regards,

Joseph

Share
10 |10000 characters needed characters left characters exceeded
Guillaume Bouzebra Nov 24, 2017 at 11:31 AM
0

Using the metadataLoaded() promise, it only executes when there is no error. If I define a wrong URL for the backend service, metadata are not loaded (I receive a network error) and the promise does not fire. And the root view is still loaded. I'm not sure I can achieve what I want..

init: function() {  
	var oModel = this.getModel("model");   
	oModel.metadataLoaded().then(function(oEvent) {         
		jQuery.sap.log.info("Metadata loaded");         
		jQuery.sap.log.info(JSON.stringify(oEvent));    

	});    

	// call the base component's init function    
	UIComponent.prototype.init.apply(this, arguments);
}
Share
10 |10000 characters needed characters left characters exceeded