cancel
Showing results for 
Search instead for 
Did you mean: 

SAPUI5 Promises do not work (for me)

former_member499177
Participant
0 Kudos

Hello experts,

New at the sport, probably I'm missing something basic here, scenario goes like this:

Within the app controller of my application (App.controller.js, onInit function) I'm performing a series of read operations on a named model, in order to have additional data ready, mainly for use by formatters in subsequent views. What I have done is using promises to make sure that data will be ready.

Problem is that application occasionally crashes when the root view (declared in manifest.json) is loaded after the app view because the formatter function can't find the data expected. Added a few break-points and observed the debugger:

a) stopping at the first read function

b) stopping at the promises condition at the end

c) stopping (and throwing error) at the formatter function at the subsequent view

d) stopping in the success function of oData read (resolve)

e) page freezes afterwards as result of the exception thrown above

"Normal" flow, when application doesn't crash goes like this:

a) stopping at the first read function

b) stopping at the promises condition at the end

c) stopping in the success function of oData read (resolve)

d) stopping at the readyToGo function (it's there just for temporary debugging)

e) stopping at the formatter function at the subsequent view

f) page display, everything in order

Judging by the above, I have reached the conclusion that my promises do not work, any ideas?

Code attached below, it's quite simple.

Best regards

Greg

sap.ui.define([
		"kristal/apps/agreements/controller/BaseController",
		"sap/ui/model/json/JSONModel"
	], function (BaseController, JSONModel) {
		"use strict";
                /* PROMISE VARIABLES */
		var oModelTypeDataDeferred = jQuery.Deferred();
		var oModelStatusDataDeferred = jQuery.Deferred();
		var oModelRoleDataDeferred = jQuery.Deferred();
		var oModelRefDataDeferred = jQuery.Deferred();
		var oModelExtOrgDataDeferred = jQuery.Deferred();
		var oModelInvolvementDataDeferred = jQuery.Deferred();
		
		return BaseController.extend("kristal.apps.agreements.controller.App", {
			onInit : function () {
                             /* CALLED AFTER PROMISES HAVE BEEN FULFILLED */
				var readyToGo = function() { 
					jQuery.sap.log.error("Ready!");
				};
	
				var oViewModel,
				fnSetAppNotBusy,
				iOriginalBusyDelay = this.getView().getBusyIndicatorDelay();
				oViewModel = new JSONModel({
					busy : true,
					delay : 0
				});
				this.setModel(oViewModel, "appView");


				fnSetAppNotBusy = function() {
					oViewModel.setProperty("/busy", false);
					oViewModel.setProperty("/delay", iOriginalBusyDelay);
				};


				this.getOwnerComponent().getModel().metadataLoaded().
					then(fnSetAppNotBusy);


					// apply content density mode to root view
				this.getView().addStyleClass(this.getOwnerComponent().getContentDensityClass());
			
				var oModel = this.getOwnerComponent().getModel("f4");
                         /* oData READ OPS */	
				oModel.metadataLoaded().then(function(){				
					// Initialize additional data 
					var sPath = "/Agreement_TypesSet";
					oModel.read(sPath, {
						success: function(oData, oResponse) {
							oModelTypeDataDeferred.resolve();
						},
						error: function(oError) {
							jQuery.sap.log.error("Error", oError);
						}
					});	
					sPath = "/Agreement_StatusesSet";
					oModel.read(sPath, {
						success: function(oData, oResponse) {
							oModelStatusDataDeferred.resolve();
						},
						error: function(oError) {
							jQuery.sap.log.error("Error", oError);
						}
					});
					sPath = "/Role_TypesSet";
					oModel.read(sPath, {
						success: function(oData, oResponse) {
							oModelRoleDataDeferred.resolve();
						},
						error: function(oError) {
							jQuery.sap.log.error("Error", oError);
						}
					});		
					sPath = "/Reference_TypesSet";
					oModel.read(sPath, {
						success: function(oData, oResponse) {
							oModelRefDataDeferred.resolve();
						},
						error: function(oError) {
							jQuery.sap.log.error("Error", oError);
						}
					});
					sPath = "/External_OrganizationsSet";
					oModel.read(sPath, {
						success: function(oData, oResponse) {
							oModelRefDataDeferred.resolve();
						},
						error: function(oError) {
							jQuery.sap.log.error("Error", oError);
						}
					});	
					sPath = "/Involvement_TypesSet";
					oModel.read(sPath, {
						success: function(oData, oResponse) {
							oModelInvolvementDataDeferred.resolve();
						},
						error: function(oError) {
							jQuery.sap.log.error("Error", oError);
						}
				        });
                           /* IF ALL PROMISES FULFILLED, PROCEED */	
				        jQuery.when(oModelTypeDataDeferred, oModelStatusDataDeferred, oModelRoleDataDeferred, oModelRefDataDeferred, + 
				        oModelExtOrgDataDeferred, oModelInvolvementDataDeferred).done().then( jQuery.proxy(readyToGo,this) );	
			}, ...remaining controller methods

Accepted Solutions (1)

Accepted Solutions (1)

former_member499177
Participant
0 Kudos

Solution found in the form of declaring and initializing the model inside Component.js and putting router initialization inside the promise condition, thanks to Jorg from SO 🙂

Answers (1)

Answers (1)

esjewett
Active Contributor
0 Kudos

I think it's less that your promises don't work and more that there is nothing making the view wait to render until your promises are resolved. The rest of the UI5 processing doesn't know about any promises you create in your controller context. It sounds like your formatter is trying to access something that doesn't yet exist. The usual way to deal with that would be to put guards in your formatter like this:

if(foo) {
   ...
}

Your formatter will be called whenever the bound value is updated, so once the value changes your formatter will be called again, the values it is looking for will exist, and all will be well.

It might be better to share a little bit more about what you're trying to accomplish here, as it seems like you're doing a lot of stuff manually (all those model reads) that should be happening automatically if you are binding to delivered controls.

former_member499177
Participant
0 Kudos

Hi Ethan, first of all, thanks for taking the time to reply.

Probably I haven't understood how promises work. I know that the view is processed before the controller, this is the reason that I have this code in the "dummy" App view that comes before the root view and not to the root view itself. What I want to achieve is to delay list loading until my data are ready. The first 2 out of the 5 read ops aim to replace codes (eg 201) with their respective descriptions (eg "Started") which reside in other tables during the creation of the initial list in the root view. What I understand is that in case I put some condition in the formatter, the best thing I can achieve is returning back the value. The values that serve as calling parameters to the formatters are already there, what's missing is the data I should return but once the list is populated, formatter will not get called again. Is there any way to make the formatter "wait"?

The rest of the oData reads serve similar purposes for fields in subsequent views after the root. If there's a smarter way to replace field values in the list coming from table A with linked data, coming from table B, I would be more than happy to use it. This one, along with doing the same thing with dropdowns (selected key not working but this belongs to another thread, so leave it 😄 ) have become proper headaches, any help here highly appreciated.

esjewett
Active Contributor

I think you should ask a new question laying out this scenario including what you've already done (without the promises part). There are all kinds of ways to make this work including associations. But in an sap.m.Select control you can bind the items displayed in the control to a different entity set than the control itself is bound to, which is probably what you want to do.