wait for async http requests before proceeding angular - angularjs

I have task groups, these groups have tasks. You can add existing tasks to your group, but also make new ones. These new ones don't have an _id yet in my mongoDB, so I have to make them first, before making my createTaskGroup call.
When I call createTaskGroup, I loop through the tasks, when there is no _id, I call "addnewtask". The problem is, that the last function "apiFactory.createTaskGroup" is called before the loop for making non existing tasks is done.
How can I wait for these functions to finish before executing createTaskGroup?
dvm.createTaskGroup = function (){
for (var i = 0; i < dvm.taskgroup.tasks.length; i++) {
if (angular.isUndefined(dvm.taskgroup.tasks[i]._id)) {
apiFactory.addNewTask(dvm.taskgroup.tasks[i].description, function (response) {
dvm.taskgroup.tasks[i] = response;
});
}
}
apiFactory.createTaskGroup(dvm.taskgroup, function (response) {
$mdDialog.hide(dvm.taskgroup);
})
};
I also tried using promises, normally I use callbacks, but I read about $q.all. So I would give it a shot. But then I can the complain about cors even it's the same call as before but with the use of promise.
dvm.createTaskGroup = function (){
var callsToWaitForBeforeContinue = [];
var tempArrayWithTasksWithId = [];
angular.forEach(dvm.taskgroup.tasks, function(task){
if(angular.isUndefined(task._id)){
callsToWaitForBeforeContinue.push(apiFactory.addNewTaskWithPromise(task.description));
}
else{
tempArrayWithTasksWithId.push(task);
}
});
$q.all(callsToWaitForBeforeContinue).then(function(req){
dvm.taskgroup.tasks = tempArrayWithTasksWithId;
angular.forEach(req, function(singlePromise){
dvm.taskgroup.tasks.push(singlePromise);
});
});
apiFactory.createTaskGroup(dvm.taskgroup, function (response) {
$mdDialog.hide(dvm.taskgroup);
});
};
Here is the http post itself.
var addNewTaskWithPromise = function(taskDescription){
var q = $q.defer();
$http.post(ENV.api + 'tasks/', taskDescription).then(function(response){
q.resolve(response);
}, errorCallback);
return q.promise;
};

You should be able to just call like so:
apiFactory.addNewTaskWithPromise(task.description).then(function(response){
dvm.taskgroup.tasks[i] = response;
apiFactory.createTaskGroup(dvm.taskgroup).then(function (response2) {
$mdDialog.hide(dvm.taskgroup);
});
});

got it to work. I return my http call as a promise, instead of making a variable for it
var addNewTaskWithPromise = function(taskDescription) {
return $http.post(ENV.api + 'tasks', {
"description": taskDescription
});
};
Call the function "createtaskgroup" in the "then" statement of my $q.all. Can't really explain the details why it works now, without the temp variable for my promise, I didn't receive a CORS error, probably someone here that could explain why.
dvm.createTaskGroup = function() {
var callsToWaitForBeforeContinue = [];
var tempArrayWithTasksWithId = [];
angular.forEach(dvm.taskgroup.tasks, function(task) {
if (angular.isUndefined(task._id)) {
callsToWaitForBeforeContinue.push(apiFactory.addNewTaskWithPromise(task.description));
} else if(angular.isDefined(task._id)) {
tempArrayWithTasksWithId.push(task);
}
});
$q.all(callsToWaitForBeforeContinue).then(function(req) {
dvm.taskgroup.tasks = tempArrayWithTasksWithId;
angular.forEach(req, function(singlePromise) {
dvm.taskgroup.tasks.push(singlePromise.data.task);
});
apiFactory.createTaskGroup(dvm.taskgroup, function(response) {
$mdDialog.hide(dvm.taskgroup);
});
});
};

Related

Call a asynchronous method after another in AngularJS

I have a $watch attached to the appId variable. SO, when appId get changed it called the init function
$scope.$watch('appId', function() {
if ($scope.appId != '') {
console.log('Inside appId watch');
$scope.init();
}
});
init() calls two service methods
$scope.init = function() {
if ($scope.appId === undefined || $scope.appId == '') return false;
$scope.getPrincipals($scope.loadPrincipals);
$scope.getSignaturesByAppId($scope.loadSignatures);
};
The methods are
$scope.getSignaturesByAppId = function (callback) {
ApplicationDataSource.getSignaturesByAppId($scope.appId, function (result) {
callback(result);
$scope.$apply();
});
};
$scope.loadSignatures = function (result) {
var signatureResultSet = angular.fromJson(result[0]);
$scope.appSignatures = signatureResultSet;
if($scope.appSignatures.length===0){
$scope.setDefaultValue();
}
else{
$scope.setValueFromObject();
}
};
$scope.getPrincipals = function (callback) {
ApplicationDataSource.getApplicationPrincipalList($scope.appId, function (result) {
callback(result);
$scope.$apply();
});
};
$scope.loadPrincipals = function (result) {
var guarantorResultSet = angular.fromJson(result[0]);
$scope.principals = guarantorResultSet;
};
The problem occurs here. In loadSignatures(), I have called a method setDefaultValue() which needs the data retrieve from loadPrincipals. So, when, loadSignatures called, principal data is not updated.
How to call the $scope.getPrincipals($scope.loadPrincipals) after $scope.getSignaturesByAppId($scope.loadSignatures) finish to retrieve data.
You can use Promises, here is an example:
var promise = callThatRunsInBackground();
promise.then(
function(answer) {
// do something
},
function(error) {
// report something
},
function(progress) {
// report progress
});
So in your code it might look like (I will leave it to you to fix as I'm not going to compile or check for syntax errors):
var getPrincipals = $scope.getPrincipals($scope.loadPrincipals);
getPrincipals.then(
function(datapayload) {
//do something with datapayload perhaps
$scope.getSignaturesByAppId($scope.loadSignatures);
});
This will make it wait until getPrincipals is finished before running getSignaturesbyAppId
What solve my issue is, I called $scope.getSignaturesByAppId($scope.loadSignatures); in loadPrincipals callback function, not in init()
$scope.loadPrincipals = function (result) {
var guarantorResultSet = angular.fromJson(result[0]);
$scope.principals = guarantorResultSet;
$scope.getSignaturesByAppId($scope.loadSignatures);
};

Resolving a promise array

I am having trouble in resolving a promise that is returned by firebase. This is an ionic - angularjs - firebase project that I am building to learn. The issue is that my function returns a promise that contains an array of 3 users but I am unable to unwrap this promise.
My code:
function eventusers(id) {
var userarr = [];
var deferred = $q.defer();
// *The code below makes 2 firebase calls and returns an array of users*
eventref.orderByChild("Eventid").equalTo(eventid).on("value", function(snap) {
var users = snap.val();
angular.forEach(users, function(value,key) {
var obj = value;
for (var prop in obj) {
if(obj[prop] == "True") {
userref.child(prop).on("value", function (snap) {
var id = snap.val().email;
userarr.push(id);
console.log(userarr); // I am able to see the list of users here
});
};
}
});
deferred.resolve(userarr);
});
return deferred.promise;
};
//The console.log shows a promise (pls see the attached pic)
console.log(eventusers(eventid));
// I tried to loop through the response using angular.forEach and also a for loop but it does
//not execute that part of the code as I do not see the response of the console.log. If I
//replace the for loop with just console.log(response), then I get an empty array.
eventusers(eventid).then(function (response) {
for (var i = 0; i <response.length; i++) {
console.log(response[i]);
}
});
Your deferred promise is resolving before the inner asynchronous action
userref.child(prop).on('value', ...
completes.
You'll need to wrap that in another deferred object then return a promise resolving all of the inner promises.
function eventusers(id) {
return $q(function(resolve) {
eventRef.orderByChild('Eventid').equalTo(id).on('value', function(snap) {
var promises = [];
snap.val().forEach(function(user) {
angular.forEach(user, function(userProp, prop) {
if (userProp === 'True') {
promises.push($q(function(resolve) {
userref.child(prop).on('value', function(snap) {
resolve(snap.val().email);
});
}));
}
});
});
resolve($q.all(promises));
});
});
}
eventusers(eventid).then(function (response) {
for (var i = 0; i < response.length; i++) {
console.log(response[i]);
}
});
Modify your code to
eventusers(eventid).then(function (response) {
var myArray = response.value;
for (var i = 0; i <myArray.length; i++) {
console.log(myArray[i]);
};
Since your array object is inside of promise object
You can also refer to Plunker

angularJS - how to perform query one by one?

I have array of objects and want to execute some requests - one for every object.
How to achieve it, since $http-service executes request in async mode?
Like async/await in C#
You can call the next request in the callback of the $http.
Something like this :
function sendRequestList(objectList) {
if (objectList.length > 0) {
var currentObject = objectList.pop();
$http.get(/* request params here */)
.then(
function() {
sendRequestList(objectList);
},
function() {
sendRequestList(objectList);
}
);
}
}
However, I don't know a way do achieve this the way you want.
Hope this help.
A+
If you want all the requests to be finished before going on, you could use angular's $q service to generate a promise, and return a result only when every request is done.
// all of this in a controller injecting $q
function fetchAll(objects) {
var deferred = $q.defer();
var done = 0;
var results = {};
for(var i=0; i < objects.length - 1; i++) {
// a trick to avoid every iteration to share same i value
(function(index) {
$http.get(/* request params here */)
.then(
function(data) {
results[index] = data;
// or results[object.anyProperty] = data;
done++;
// ensure all calls are successful
if (done === objects.length) {
deferred.resolve();
}
},
function() {
deferred.reject();
}
);
})(i);
}
return deferred.promise;
}
// and call it like this
fetchAll(objects)
.then(function success(result) {
// continue your business
}, function error(result) {
// handle error
});

Catching errors at the end of a chain of promises

I am using a factory to query data from Parse.com, this occurs many times in my ionic app and would like to apply a little more DRY to my code.
To call data I am using:
ParseFactory.provider('Clients', query).getAll().success(function(data) {
$localStorage.Clients = data.results;
}).error(function(response) {
errorFactory.checkError(response);
});
And often run many of these back to back to get data from different classes on the loading of a page.
Is it possible to use one error block at the end of all of these? like this:
ParseFactory.provider('Favourites', query).getAll().success(function(data) {
$localStorage.Favourites = data.results;
})
ParseFactory.provider('Somethings/', query).getAll().success(function(data) {
$localStorage.Programmes = data.results;
})
ParseFactory.provider('UserItems', query).getAll().success(function(data) {
$localStorage.UserExercises = data.results;
})
ParseFactory.provider('Customers', query).getAll().success(function(data) {
$localStorage.Clients = data.results;
}).error(function(response) {
errorFactory.checkError(response);
});
You could create helper method:
function query(resource, query) {
function querySucceeded(data) {
$localStorage[resource] = data.results;
}
function queryFailed() {}
ParseFactory.provider(resource, query)
.getAll()
.success(querySucceeded)
.error(queryFailed);
}
and, then just call:
query('Favourites', query);
query('Customers', query);
and so on.
Alternatively, you could factor queryFailed out, as such:
function query(resource, query) {
function querySucceeded(data) {
$localStorage[resource] = data.results;
}
return ParseFactory.provider(resource, query)
.getAll()
.success(querySucceeded);
}
function queryFailed() {
}
$q.all([
query('Favourites', query1),
query('UserItems', query2)])
.error(queryFailed);
$q.all takes an array (or object) of promises, and returns a single one.
The returned promise is resolved when all the original promises are resolved. It's rejected as soon as one of the original promises is rejected.
So, what you can simply do is something like
var request = function(path) {
return ParseFactory.provider(path, query).getAll();
};
var promises = {
favourites: request('Favourites'),
programmes: request('Somethings/'),
exercises: request('UserItems'),
customers: request('Customers')
};
$q.all(promises).then(function(results) {
$localStorage.Favourites = results.favourites.data.results;
// ...
}).catch(function() {
// ...
});

Angular: Rewriting function to use promise

I'm using an Angular factory that retrieves data from a feed and does some data manipulation on it.
I'd like to block my app from rendering the first view until this data preparation is done. My understanding is that I need to use promises for this, and then in a controller use .then to call functions that can be run as soon as the promise resolves.
From looking at examples I'm finding it very difficult to implement a promise in my factory. Specifically I'm not sure where to put the defers and resolves. Could anyone weigh in on what would be the best way to implement one?
Here is my working factory without promise:
angular.module('MyApp.DataHandler', []) // So Modular, much name
.factory('DataHandler', function ($rootScope, $state, StorageHandler) {
var obj = {
InitData : function() {
StorageHandler.defaultConfig = {clientName:'test_feed'};
StorageHandler.prepData = function(data) {
var i = 0;
var maps = StorageHandler.dataMap;
i = data.line_up.length;
while(i--) {
// Do loads of string manipulations here
}
return data;
}
// Check for localdata
if(typeof StorageHandler.handle('localdata.favorites') == 'undefined') {
StorageHandler.handle('localdata.favorites',[]);
}
},
};
return obj;
});
Here's what I tried from looking at examples:
angular.module('MyApp.DataHandler', []) // So Modular, much name
.factory('DataHandler', function ($rootScope, $q, $state, StorageHandler) {
var obj = {
InitData : function() {
var d = $q.defer(); // Set defer
StorageHandler.defaultConfig = {clientName:'test_feed'};
StorageHandler.prepData = function(data) {
var i = 0;
var maps = StorageHandler.dataMap;
i = data.line_up.length;
while(i--) {
// Do loads of string manipulations here
}
return data;
}
// Check for localdata
if(typeof StorageHandler.handle('localdata.favorites') == 'undefined') {
StorageHandler.handle('localdata.favorites',[]);
}
return d.promise; // Return promise
},
};
return obj;
});
But nothing is shown in console when I use this in my controller:
DataHandler.InitData()
.then(function () {
// Successful
console.log('success');
},
function () {
// failure
console.log('failure');
})
.then(function () {
// Like a Finally Clause
console.log('done');
});
Any thoughts?
Like Florian mentioned. Your asynchronous call is not obvious in the code you've shown.
Here is the gist of what you want:
angular.module("myApp",[]).factory("myFactory",function($http,$q){
return {
//$http.get returns a promise.
//which is latched onto and chained in the controller
initData: function(){
return $http.get("myurl").then(function(response){
var data = response.data;
//Do All your things...
return data;
},function(err){
//do stuff with the error..
return $q.reject(err);
//OR throw err;
//as mentioned below returning a new rejected promise is a slight anti-pattern,
//However, a practical use case could be that it would suppress logging,
//and allow specific throw/logging control where the service is implemented (controller)
});
}
}
}).controller("myCtrl",function(myFactory,$scope){
myFactory.initData().then(function(data){
$scope.myData = data;
},function(err){
//error loudly
$scope.error = err.message
})['finally'](function(){
//done.
});
});

Resources