Skip to Content
0

SAP Fiori: How to bind async loaded model to UI?

Feb 10, 2017 at 11:04 AM

597

avatar image
Former Member

How can I use a model that was loaded by an async AJAX request in the UI?
What is the approach to load the model and even where?

I've tried to load the model in the Component.js (based on the QuickStartApplication template). Doesn't work. Because the request grabs data from another server it has to be async anyway. The AJAX call returns the data but I can't wait for it in neither in the component.js nor in the controller nor in the model.js.

My model.js including the async call looks like:

sap.ui.define([
	"sap/ui/model/json/JSONModel"
], function(JSONModel) {
	"use strict";
		function ajaxCallToCMISRepository(callbackResult) {
			jQuery.sap.log.info("Entering ajaxCallToCMISRepository");
			var resultObjects = "({})";
			var repoUser = "admin";
			var repoPass = "admin";
	    	var repoURL = "https://myserver:8443/cmis/root";
	    	jQuery.sap.log.info("Load JSON from Repository " + repoURL);
			var method = "GET";
			jQuery.sap.log.info("AJAX call to: " + repoURL);
			var dfd = new $.Deferred();
			$.ajax({
				type: method,
				url: repoURL,
				async: false,
				data: "{}",
				crossDomain: true,
				dataType: "jsonp",
				beforeSend: function (xhr){ 
					xhr.withCredentials = true;
				    xhr.setRequestHeader("Authorization", "Basic " + btoa(repoUser + ":" + repoPass)); 
				},
				success:function(data) {
            		callbackResult(data);
				},
				error : function (xhr, status, error) {
					jQuery.sap.log.info("ERROR xhr: " + xhr);
					jQuery.sap.log.info("ERROR status: " + status);
					jQuery.sap.log.info("ERROR error: " + error);
					var keys = $.map(xhr, function(item, key) {
						jQuery.sap.log.info("!!!!!!! " + key +  " ==> " + item);
					});
				}
			});
			//jQuery.sap.log.info("Return resultObjects: " + resultObjects);
			//return resultObjects;
		}


	return {
		loadRepositoryModel: function() {
			jQuery.sap.log.info("Entering loadRepositoryModel in model.js)");
			var resultJSON = "";
			ajaxCallToCMISRepository(function(data) {
    			resultJSON = data;
    			//resultJSON = getFolderNamesFromRepository(resultJSON["objects"]);
		    	var oModel = new JSONModel(resultJSON);
		    	oModel.setData(resultJSON);
				oModel.setDefaultBindingMode(sap.ui.model.BindingMode.OneWay);
				jQuery.sap.log.info("RETURN from loadRepositoryModel()!" + resultJSON.getJSON());
				return oModel;
			});
			//return resultJSON;
		}
	};
});

The related part in the Component.js is:

		init: function() {
			// call the base component's init function
			UIComponent.prototype.init.apply(this, arguments);
			var cmisModel = new JSONModel(models.loadRepositoryModel());
			this.setModel(cmisModel, "repoModel");
                        jQuery.sap.log.info("cmisModel -> " + cmisModel.getJSON());

But while the AJAX returns the model, meaning the log of resultJSON.getJSON() in the model.js returns the result, the cmisModel.getJSON() in the component.js is always empty because it does not wait.

What is the best way to load and finally to display async data in the UI?

10 |10000 characters needed characters left characters exceeded
* Please Login or Register to Answer, Follow or Comment.

5 Answers

Srikanth KV Feb 10, 2017 at 06:08 PM
0

When using AJAX calls or ODATA aggregation bindings its better to use promises to notify that data load is completed.

In your case 'model.js' contains the function 'success' indicating that data is loaded from AJAX call, in this promise function you can set the model 'cisModel'

or

Raise an event from the promise function and catch the same in 'Component.js', in the event handler set the model 'cisModel'

Share
10 |10000 characters needed characters left characters exceeded
avatar image
Former Member Feb 10, 2017 at 11:06 PM
0

Thank you for the answer. But even if I try to set the model in the success function, because of the ASYNC call the rest of the model including the UI has been already loaded before the AJAX call has finished. So I still facing the same issue.

success:function(data) {
       sap.ui.getCore().setModel(data, "cmisModel");

Maybe, I have to refresh the table after loading? But within the success I don't have the controller available. What is the best way to do that?

Show 1 Share
10 |10000 characters needed characters left characters exceeded

normally in this case we can pass component instance to model.js so that model can be set on Component

Use below code in Component.js

init: function(){
      UIComponent.prototype.init.apply(this, arguments);
      models.loadRepositoryModel(this);      //pass component instance to method in models
} //Below code for success method in loadRepositoryModel loadRepositoryModel: function(Component) { //Component reference passed to method success:function(data) { Component.setModel(data, "cmisModel"); } }
0
avatar image
Former Member Feb 13, 2017 at 09:23 AM
0

Tried the $.Deferred(); approach along with the attachRequestCompleted event. Looks like this works only fine for models loaded with .loadData(...). But I have a async request (from another domain) and I'm using the function setData(ajaxResultJSON) to fill the model. With that the attachRequestCompleted event is not fired.
This is an example that I found, it does exactly what I need but it uses the .loadData() function: https://jsbin.com/miripajozu/2/edit?html,js,console,output

The issue is, that the model is not available at this time, because it is still loading. So, having the $.Deferred(); approach in my controller, the model is undefined at this time and therefore the .attachRequestCompleted() fails... and the wait does not work at all.
Next try was to instantiate the model in the controller before (according to the example above), setting an empty model, like:

sap.ui.getCore().setModel(new JSONModel(),"cmisModel");

Having that, the $.Deferred(); approach does not work either, because attachRequestCompleted() is always true for the promise. It is not fired again when the async requests updates the data with setData().

This is the code I tried in my controller.js:

var cmisModel = sap.ui.getCore().getModel("cmisModel");
var remoteCheck = this.getPromise(cmisModel,"/objects");
$.when(remoteCheck).done(function(){ 
	jQuery.sap.log.info("Model loaded!");
	this.byId("myRepoTable").setModel(cmisModel, "cmisModel");
}.bind(this));

The getPromise function:

getPromise : function (oModel, pathToTestForData){
	jQuery.sap.log.info("IN: getPromise, " + oModel + ", " + pathToTestForData);
	var deferred = $.Deferred();
	if (oModel.getProperty(pathToTestForData)){
		jQuery.sap.log.info("ok");
		deferred.resolve(); //Data already loaded
	} else {
		jQuery.sap.log.info("wait");
		oModel.attachRequestCompleted(deferred.resolve()); //Waiting for the event
	}	
	jQuery.sap.log.info("OUT: getPromise!");
	return deferred.promise();
}

Any suggestions how I can solve it?

Share
10 |10000 characters needed characters left characters exceeded
Dominique Pierre Feb 14, 2017 at 10:18 AM
0

Hi,

This should not be complicated, use data binding and set the data to the model when you get it from the server. Here's an example: http://plnkr.co/edit/A5NUMbGa1fOz03dZ8qIJ?p=preview

Cheers,

Pierre

Show 4 Share
10 |10000 characters needed characters left characters exceeded
Former Member

Hi,

thanks, but it's still not working... tried it now with a simpler model but still dont know, how to bin the model within JavaScript to an ObjectListItem?

my component.js

init: function() {
	UIComponent.prototype.init.apply(this, arguments);
	sap.ui.getCore().setModel(new JSONModel({}), "activeUser");
	models.setCurrentUserModel();
}

my model.js

return {
	setCurrentUserModel: function() {
	var repoUser = "myuser";
	var repoPass = "mypass";
	var repoURL = "https://our.company.com:8443/api/user";
	$.ajax({
		type: "GET",
		url: repoURL,
		async: true,
		crossDomain: true,
		beforeSend: function (xhr){ 
		   xhr.withCredentials = true;
		   xhr.setRequestHeader("Authorization", "Basic " + btoa(repoUser + ":" + repoPass)); 
		},
		success:function(data) {
	 		var oModel = new JSONModel();
			oModel.setData(data);
			oModel.setDefaultBindingMode(sap.ui.model.BindingMode.TwoWay);
			sap.ui.getCore().setModel(oModel, "activeUser");
		},
		error : function (xhr, status, error) {
			jQuery.sap.log.info("ERROR xhr: " + xhr);
			jQuery.sap.log.info("ERROR status: " + status);
			jQuery.sap.log.info("ERROR error: " + error);
		}
	});
	}
};

The JSON that is returned has the following format, its just one entry:

{"id":1001,"firstName":"Henry","lastName":"Myers","email":"h.myers@company.com","externalId":null, "authorization: [ {...}, {...}]}

All I want to display in the sap.m.ObjectListItem is the "firstName" and the "lastName" as title information. So, I tried to read it in the init() method of the controller view:

return Controller.extend("QuickstartApplication1.controller.View1", {
	onInit : function () {
        	var userObjectListItemTile = this.byId("tileUserAccount"); //this does exists and is of type ObjectListItem, like: <ObjectListItem title="User Account" intro="User Account" icon="sap-icon://account" id="tileUserAccount" activeIcon="sap-icon://account"/>
	        userObjectListItemTile.bindProperty("title", "acitivUser>/firstName");
        }
});	

Nothing happens, even the model is loaded? Any thought what goes wrong?

Thanks!

0

2 things:

- you have to bind the items aggregation if it's not done in the view xml

- there's a typo in your code (acitivUser):

userObjectListItemTile.bindProperty("title", "acitivUser>/firstName");

Check this and if it still doesn't work then please copy/paste your view xml.

0
Former Member
Dominique Pierre

Thanks, for the hint regarding the typo. I have not yet bound the items aggregation in the view xml. How can I achieve this in the code?

0

Well maybe you should start with the tutorial if you don't know how to use data binding: https://sapui5.hana.ondemand.com/#docs/guide/bf71375454654b44af01379a3c3a6273.html

0
Jun Wu Feb 14, 2017 at 03:48 PM
0

binding only need model, not the data.

Share
10 |10000 characters needed characters left characters exceeded