Skip to Content
avatar image
Former Member

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

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?

Add comment
10|10000 characters needed characters exceeded

  • Get RSS Feed

5 Answers

  • Feb 10, 2017 at 06:08 PM

    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'

    Add comment
    10|10000 characters needed characters exceeded

  • avatar image
    Former Member
    Feb 10, 2017 at 11:06 PM

    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?

    Add comment
    10|10000 characters needed 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"); } }
  • avatar image
    Former Member
    Feb 13, 2017 at 09:23 AM

    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?

    Add comment
    10|10000 characters needed characters exceeded

  • Feb 14, 2017 at 10:18 AM

    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

    Add comment
    10|10000 characters needed characters exceeded

  • Feb 14, 2017 at 03:48 PM

    binding only need model, not the data.

    Add comment
    10|10000 characters needed characters exceeded