deferred promise not working - angularjs

I am using promise in angular for my web app like this-
var deferred = $q.defer();
On Success -
deferred.resolve(profile); // profile = JSON object
On Failure -
deferred.reject(1); // 1 or no value returned
At end of function -
return deferred.promise;
Then I am handing for this returned Promise Object to call another method.But it doesn't call. While if i use Callback(error,success) it works fine.Can somebody suggest what is wrong with my promise.
Code Snippet-
function open() { // for initializing DB,getting called from service
var deferred = $q.defer();
var options = {
Encryption: {
encryptKey: false, // optional encrypt primary key
secrets: [{
name: 'dddd',
key: 'xxxxxxxxxx'
}]
}
};
var schema = {
stores:[{
name:'profile',
encrypted: true
}]
};
var db = new ydn.db.Storage('nowconferdb', schema, options);
db.onReady(function() {
console.log('DB is initialized'); // getting this
profilestorage.setDB(db); // getting called and setting DB in profilestorage service
deferred.resolve(true);
});
db.addEventListener('fail', function (event) {
var err = event.getError();
if (err.name == 'versionchange') {
console.log('The application is updated, please refresh to upgrade.');
profilestorage.setup(db);
} else {
console.log('connection failed with ' + err.name + ' by ' + err.message);
db = null; // no operation can be placed to the database instance
}
deferred.reject(false);
});
return deferred.promise;
}
This is my calling method -
storageservice.open().then(function() {
console.log('post initializing storageservice'); // not getting it.
});
Thanks a lot for your sincere efforts.

What you should do is to call your function inside the success callback of the promise. Assumming you assign your promise to deferred variable:
deferred.then(
function (data) {
// Here you call your other function/method
// It will be called when 'deferred' promise is resolved
anotherFunction();
},
function (error) {
// Handle the error
}
);
I hope this helps, even though the information you give is not enough.

Related

Http service delay processing until after data is returned

I work on my angularjs project.
I created this service:
(function () {
"use strict";
angular.module("manageItems").factory("manageItemsService", ["$http", "config", manageItemsService]);
function manageItemsService($http, config) {
var service = {
getNewItems: getNewItems,
};
return service;
function getNewItems(session, mapName) {
return $http.get(serviceUrl + 'getNewItems/' + session + "/" + mapName);
}
}
})();
And here how I call the service from controller:
function getNewItems() {
manageItemsService.getNewItems(mapguideService.mapName, mapguideService.sessionId).then(function (result) {
self.currentItems = result.data;
})
}
I need to make service to delay while the response returned.
How can I change servicefunction to make it wait until self.currentItems property is populated by data?
First I need say that http requests are actually performed asynchronously so as not to stop the application while the result is returned.
So you have two options, use the angular pattern to adjust your method in order to treat the result, so you must pass a callback function to the service, so that the service, not the controller make the association. It would be something like:
Service:
(function () {
"use strict";
angular.module("manageItems").factory("manageItemsService", ["$http", "config", manageItemsService]);
function manageItemsService($http, config) {
var service = {
getNewItems: getNewItems,
};
return service;
function getNewItems(session, mapName, callback, errorCallback) {
$http.get(serviceUrl + 'getNewItems/' + session + "/" + mapName).then(callback, errorCallback);;
}
}
})();
Controller:
function getNewItems() {
manageItemsService.getNewItems(mapguideService.mapName, mapguideService.sessionId, function (result) {
//this callback will be called asynchronously when the response is available
self.currentItems = result.data;
}, function(error) {
// called asynchronously if an error occurs
// or server returns response with an error status.
})
}
The second option is to totally not recommended, inserting a loop while the result is expected... (to bad)
I hope I have helped!
Then you could be put up .then on getNewItems $http call. And based on retrieved response data, decide whether to return data or call another service method.
function anotherFunction(){
return $http.get(url);
}
function getNewItems(session, mapName) {
return $http.get(serviceUrl + 'getNewItems/' + session + "/" + mapName).then(function successCallback(response){
var data = response.data;
//call another function if data is empty
if(!data.length)
return anotherFunction(); //make sure another function should return promise
return data;
});
}
What the code needs to do is chain promises.
To make the getNewItems function chainable, return the derived promise:
function getNewItems() {
//vvvv RETURN promise
return manageItemsService.getNewItems(mapguideService.mapName, mapguideService.sessionId)
.then(function (response) {
self.currentItems = response.data;
//RETURN value to chain
return response.data;
});
};
Then use the returned promise to chain more operations:
getNewItems().then( function(currentItems) {
//Evaluate current Items
if ( ok ) {
return "DONE";
} else {
//RETURN to chain something else
return getOtherItems();
};
}).then( function(otherItems) {
if (otherItems == "DONE") return;
//ELSE
self.otherItems = otherItems;
//Do further chaining
});
Because calling the .then method of a promise returns a new derived promise, it is easily possible to create a chain of promises.
It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain. This makes it possible to implement powerful APIs.
— AngularJS $q Service API Reference - Chaining Promises

Promise Chains in angularjs

I am uploading attachments using rest api in SharePoint 2013,for this I need to call upload attachment method on synchronous.
Because If I call upload attachment method asynchronous I am getting 409 conflict error.
How to chain promise objects in for loop.i.e I want to call second attachment method in first attachment success and so on..
Please help me in best approach of chaining of promises in for loop.
Common method for saving attachments:
var saveFileAngularJS = function (file, url) {
var deferred = $q.defer();
getFileBuffer(file).then(function (fileArrBuffer) {
$http({
method: 'POST',
url: baseUrl + url,
headers: {
'Accept': 'application/json;odata=verbose',
'Content-Type': undefined,
'X-RequestDigest': jQuery("#__REQUESTDIGEST").val()
},
data: new Uint8Array(fileArrBuffer),
transformRequest: []
}).then(function successCallback(data) {
deferred.resolve(data);
alert('Successfully saved.', data);
}, function errorCallback(error) {
deferred.reject(error);
alert('Failed to save!!!.', error);
});
});
return deferred.promise;
};
Method calling :
for (var i = 0; i < $scope.files.length; i++) {
var file = $scope.files[i]._file;
var response = lssDealService.insertAttachment(transactionId, file);
}
var insertAttachment = function (dealId, file) {
var attachmentUrl = listEndPoint + "/GetByTitle('TransactionList')/GetItemById(" + dealId + ")/AttachmentFiles/add(FileName='" + file.name + "')";
return baseService.saveFile(file, attachmentUrl);
};
Insert attachment will call SaveFile method.
I want to run this for loop sequentially, once the loop has been completed I need to process all promises and display success message to user.
Please help me to writing the chaining promises in effective way.
Lets say you have the attachements as an array,
function uploadMyAttachements() {
return myAttachements.reduce(function(promise, attachment) {
return promise.then(function () {
return upload(attachment);
})
.then(function(result) {
console.log('RESULT FOR LAST UPLOAD', result);
});
}, Promise.resolve());
}
function upload(attachment) {
//upload the attachment to sharepoint
//and return a promise here
}
uploadMyAttachements().catch(function(err) {
//if anything in the promise chain fails
//it stops then and there and CATCHED here
});
Now whats happening here, using the Array.reduce, we create a chain of promises like shown below
upload(0).then(handleResult_0).upload(1).then(handleResult_1)....
and it execute one by one as you expected
Throwing my 2 pennies:
$scope.attachments = []; //modified via binding.
function uploadAttachments(){
//Reduce the files array into a promise array with the uploadOne method
//then return the promise when every promise has been resolved or one has rejected.
return $q.all($scope.attachments.reduce(uploadOne, []));
}
function uploadOne(file){
//Upload one, return promise. Use $http or $resource.
}
//Note - a more advanced way of doing this would be to send the files as batch (one
//$http post) as FormData. There are some good wrappers for angular.
$scope.upload = function(){
uploadAttachments().then(function(results){
//Array of results
}).catch(function(e){
//Error handler
});
}

MobileFirst: Adapter Authentication calls Submit success repeatedly in Angular App

Environment:
MFPF v7.0
Eclipse: Luna SR.2 (4.4.2)
Windows 7
I face an strange issue. I am using adapter based authentication in one of my Angular based project.
The app authenticates well, but it repeatedly calls the submitSuccess.
I guess, it has something with the way Angular works, either I should use Challenge Handler as a Service or Controller. Because the way MobileFirst detects & handle instances of a/any handler objects. And that cause reference mis-match to dispose off or execute the relevant functions at appropriate time.
Currently I use it as a service.
Below is the challenge handler that I use.
define(['angular'], function(angular){
var loginChallengeHandler = angular.module('webApp.loginChallengeHandler', [])
.service('loginChallengeHandler', function(){
var _this = this;
_this.AuthRealmChallengeHandler = WL.Client.createChallengeHandler("AdapterAuthRealm");
_this.AuthRealmChallengeHandler.isCustomResponse = function(response) {
console.error("AuthRealmChallengeHandler.isCustomResponse:: " , response);
if (!response || !response.responseJSON || response.responseText === null) {
return false;
}
if (typeof(response.responseJSON.authRequired) !== 'undefined' || response.responseJSON.authRequired == true){
return true;
} else {
return false;
}
};
_this.AuthRealmChallengeHandler.handleChallenge = function(response){
console.error("AuthRealmChallengeHandler.handleChallenge:: " , response);
var authRequired = response.responseJSON.authRequired;
if (authRequired == true){
console.error("------Auth Required----- ");
_authenticationFailed(response);
} else if (authRequired == false){
console.error("------Auth PASSED ----- ");
//Now tell WL Authentication that user has been verified successfully so that it finishes the authentication process
_this.AuthRealmChallengeHandler.submitSuccess();
console.error("------ submitSuccess ----- ");
}
};
_this.AuthRealmChallengeHandler.userLogin = function(dataObjRef) {
var loginStatePromise = $q.defer();
_this.AuthRealmChallengeHandler.submitAdapterAuthentication(options,{
onFailure: function (error) {
loginStatePromise.resolve({ state:false , val: "" });
console.log("submitAdapterAuthentication Failed called ", error);
},
onSuccess: function(response) {
console.log("-> submitAdapterAuthentication onSuccess called " , response);
loginStatePromise.resolve({ state: _state , val: _msg });
},
timeout: 30000
});
return loginStatePromise.promise;
};
_this.AuthRealmChallengeHandler.logout = function (){
var userLogoutPromise = $q.defer();
WL.Client.logout("AdapterAuthRealm",{
onSuccess: function(){
console.log("onSuccess");
userLogoutPromise.resolve(true);
},
onFailure: function(){
console.log("onFailure");
userLogoutPromise.resolve(false);
},
timeout: 30000
});
return userLogoutPromise.promise;
};
var _authenticationFailed = function(response){
console.error("_authenticationFailed:: " , response);
//register failure request
_this.AuthRealmChallengeHandler.submitFailure();
};
});
return loginChallengeHandler;
});
I have also tried to bind the handler object with window object, so that it can access the handler's instance methods correctly.
Like:
window.AuthRealmChallengeHandler = WL.Client.createChallengeHandler("AdapterAuthRealm");
window.AuthRealmChallengeHandler.isCustomResponse = function(response) {
.
.
But still same issue.
I solved this issue and here is my solution for anyone facing similar issue in future.
Solution Description (few words)
As per my understanding, the IBM MobileFirst is expecting only one challenge-handler instance (the object that is create via createChallengeHandler function) to exists in the app. So most probably it assumes that the instance would be hooked into the window object.
Now based on this knowledge, we can see that above code is not working even we made instance through service ( i.e. singleton per angular app). Why ? Because, now the handler object becomes accessible via another reference, and this caused issues in resolving the handler references within the WL APIs.
So I just changed a bit of code (hooked it into window) so that WL APIs could reach the correct handler instance and clean-up the requests poll before marking the call successful and dispose off all the cached requests.
One more thing I would suggest.
Create only one handler instance in your client code
Create it as a service or factory - both are singletons in angularjs
Avoid using controllers, because there can be many controller instances within the angular app and it would lead to multiple handler references
And importantly trust IBM MobileFirst :)
Working Challenge Handler as Service
define(['angular'], function(angular){
'use strict';
var loginChallengeHandler = angular.module('webApp.loginChallengeHandler', [])
.service('loginChallengeHandler', function(){
//NOTE:- Below Must be bind with Window Object, otherwise will NOT work as per challenge handlers default behavior
window.AuthRealmChallengeHandler = WL.Client.createChallengeHandler("AdapterAuthRealm");
AuthRealmChallengeHandler.isCustomResponse = function(response) {
if (response && response.responseJSON && typeof (response.responseJSON.authStatus) === "string"){
return true;
} else {
return false;
}
};
AuthRealmChallengeHandler.handleChallenge = function(response){
var authStatus = response.responseJSON.authStatus;
if (authStatus === "failed"){
console.error("------Auth Required----- ");
_authenticationFailed(response);
} else if (authStatus === "passed"){
console.error("------Auth PASSED ----- ");
//do something here like change page etc.
//Now tell WL Authentication that user has been verified successfully so that it finishes the authentication process
AuthRealmChallengeHandler.submitSuccess();
}
};
AuthRealmChallengeHandler.userLogin = function(dataObjRef) {
var loginStatePromise = $q.defer();
AuthRealmChallengeHandler.submitAdapterAuthentication(options,{
onFailure: function (error) {
loginStatePromise.resolve({ state:false , val: "" });
},
onSuccess: function(response) {
loginStatePromise.resolve({ state: _state , val: _msg });
},
timeout: 30000
});
return loginStatePromise.promise;
};
AuthRealmChallengeHandler.logout = function (){
var userLogoutPromise = $q.defer();
WL.Client.logout("AdapterAuthRealm",{
onSuccess: function(){
//$state.go("home.login");
userLogoutPromise.resolve(true);
},
onFailure: function(){
userLogoutPromise.resolve(false);
},
timeout: 30000
});
return userLogoutPromise.promise;
};
var _authenticationFailed = function(response){
//register failure request
AuthRealmChallengeHandler.submitFailure();
};
});//end of service
return loginChallengeHandler;
});
Adapter
function onAuthRequired(headers, errorMessage){
errorMessage = errorMessage ? errorMessage : null;
return {
authStatus: "failed",
errorMessage: errorMessage
};
}
function Login(request){
if(request){
/* IF user credentials are Verified Correctly
* and user is authenticated then create User Identity
* and return success message if it is required by client app.
*/
userIdentity = {
userId: "abc",
displayName: "ABc",
attributes: {}
};
WL.Server.setActiveUser("AdapterAuthRealm", userIdentity);
WL.Logger.error("Auth Successful:");
return {
authStatus: "passed",
submitResponse: "send a Success message in case is required on client-side"
};
}else{
return onAuthRequired(null, "send an error message if required on client side");
}
}
I faced the same issue with adapter based authentication but I was using pure javascript, so no angular. From that I can tell you it's a MobileFirst issue and nothing related to angular.
This might sound contradictory to the documentations but don't call the submitSuccess function, just call your code on successful authentication. It will work fine and authenticate properly.
Also, make sure that you only have the security test set on the specific functions that you use after auth and not on the auth function itself.
Your code seems fine to me but I'm not that good in angular.

$httpBackend return promise value issue

I want to implement a login function using AngularJS and my backend is in Rails. i decided to implement it using the $httpBackend but I have a problem.
When it gets into the $httpBackend function, it does update the token with the latest token from the database but i need to return the value to my services of which that doesnt seem to be happening. I know this has to do with promise and deferred etc but i am not well conversant with those.
SO this is my code
var authorized = false;
var token;
$httpBackend.whenPOST('https://login').respond(function(method, url, data) {
var loginDetails = data;
var d= $q.defer();
function startToken(loginDetails) {
getTokens.newToken(loginDetails).then(function(result) {
if(result.length > 0) {
var updateDB = "UPDATE preferences SET value='"+result[0].token+"' WHERE description='token'";
$cordovaSQLite.execute(db, updateDB).then(function(res) {
var updateDB1 = "UPDATE preferences SET value='true' WHERE description='logged_in'";
$cordovaSQLite.execute(db, updateDB1).then(function(res) {
var query = "SELECT description, value FROM preferences";
$cordovaSQLite.execute(db, query).then(function(res) {
if(res.rows.length > 0) {
if(res.rows.item(3).value!=null || res.rows.item(3).value!='') {
getTokens.getCRMToken(res.rows.item(2).value).then(function(resulttoken){
if(resulttoken[0].token == res.rows.item(3).value) {
token = res.rows.item(3).value;
}
d.resolve(token)
});
}
} else {
console.log("No results found");
}
}, function (err) {
console.error(err);
});
}, function (err) {
console.error(err);
});
}, function (err) {
console.error(err);
});
}
else {
console.log("reject")
d.reject(result);
}
}, 1000);
return d.promise;
}
var a = startToken(loginDetails).then(function(token) {
// in here the value for token is correct i then go ahead to set the value for authorized and resolve it
console.log(token)
if(token.length > 0){
console.log("authorized true")
authorized = true;
d.resolve(token, authorized)
}
else
{
console.log("authorized false")
authorized = false;
d.reject(token, authorized)
}
return d.promise;
})
// this is where i have my issue. all i want to do is to just check if the value for authorized is true, if yes, return the value for token.
//authorized = true;
//return [200 , { authorizationToken: token }];
});
Complete rewrite
Sadly, I think the short answer is that you cannot use promises with $httpBackend. See this discussion: https://github.com/angular/angular.js/issues/11245
In my original answer, I didn't recognize that you were using the $httpBackend mock as I merely concentrated on your incorrect promise code. The information on promises (https://docs.angularjs.org/api/ng/service/$q) is still valid.
The unit test version of ngMock does not handle promises. If you are using a version of $httpBackend which can handle promises, you need to return the promise, not the [200, "status"].
However, that said, in your rewrite, you also reuse the same promise after it has been resolved which is incorrect. You can only resolve or reject a defer once. So you need to either chain your then() functions or create a new defer. Also, you didn't actually need to create a defer since the newToken() function actually returns a promise.

Chaining Angular Promises

I am having some trouble chaining promises in Angular. What I want to do is fetch my project object from the API, then check if the project owner has any containers, if they do, trigger the another GET to retrieve the container. In the end the container assigned to scope should either be null or the object retrieved from the API.
Right now, this example below resolves immediately to the second then function, and I get the error, TypeError: Cannot read property 'owner' of undefined. What am I doing wrong?
$http.get('/api/projects/' + id).then(function (data) {
$scope.project = data.project;
return data.project;
}).then(function (project) {
var containers = project.owner.containers;
if (containers.length) {
return $http.get('/api/containers/' + containers[0]);
} else {
return null
}
}).then(function (container) {
$scope.container = container;
});
Ah, turns out the data from passed into then is inside a field, so I needed to do
$scope.project = data.data.project;
return data.data.project;
Your example code works, but what if the $http call fails because of a 404? Or you want to later want to add some extra business logic?
In general you want to handle 'negative' cases using a rejecting promise, to have more control over the chaining flow.
$http.get('/api/projects/' + id).then(function (data) {
$scope.project = data.data.project;
return data.data.project;
}).then(function (project) {
var containers = project.owner.containers;
if (containers.length) {
return $q.reject('containers empty');
}
return $http.get('/api/containers/' + containers[0]);
}).then(function (container) {
$scope.container = container;
}).except(function (response) {
console.log(response); // 'containers empty' or $http response object
$scope.container = null;
});

Resources