Catching errors at the end of a chain of promises - angularjs

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() {
// ...
});

Related

AngularJs Service called from two places returns same value

I am new to angular and pardon my ignorance if any. I am trying to create a simple service that will do a get funtionality and serve data into array.
The problem i am having is, no mattter what i do - i always get the same data for any parameter i pass.
Here is my sample service
function myService($http, $q) {
var service = {
getSomeData: getSomeData:
};
var def = $q.defer();
return service;
function getSomeData(category) {
if (category === 'books') {
url = 'http://www.someurl1';
} else {
url = 'http://www.someurl2'
};
$http.get(url, {
params: {
'type': category
}
}).success(function(data) {
def.resolve(data);
}).error(function() {
def.reject('Failed to get data');
});
return def.promise;
}
}
})();
Once i have this, in my controller, i am trying to call it for sample purposes like this
$scope.someData = [] ;
$scope.someData.push(myService.getSomeData('DVDs');
$scope.someData.push(myService.getSomeData('books');
Now when i my look at my $scope.someData,
i have an array of two objects - the problem being that they are always the same and doesnt have data specific to books and dvds.
Another minor issue I have is my object someData has
array --> Promise -- > $$state --> value
which then has the actual data. how can i get the data directly.
i tried return def.promise(data);
One problem is you are only creating one promise outside the getSomeData function.
A promise can only be resolved once. You need a new promise for each request.
Also $http already returns a promise but you can't push it directly into an array as data.
your code should look more like:
Service:
function myService($http, $q) {
var service = {
getSomeData: getSomeData
};
return service;
function getSomeData(category) {
if (category === 'books') {
url = 'http://www.someurl1';
} else {
url = 'http://www.someurl2'
};
return $http.get(url, {
params: {
'type': category
}
}).then(function(response) {
return response.data;
}).catch(function(err) {
console.log("Ooops", err)
});
}
}
Controller
$scope.someData = [] ;
myService.getSomeData('DVDs').then(function(data){
data.forEach(function(item){
$scope.someData.push(item);
});
});

How to get .notify() in chaining promises from $q.all?

I am using chained promises in angular js with $q service and it's working fine except the information of progressCallback ? let me draw what I have done so far?
calling function from my controller in below chainable promise way
fun1()
.then(resp1){
return fun2(resp1.id);
})
.then(resp2){
return $q.all([fun3(resp2.id),fun4(resp2.name)]);
})
.then(function(resp34){
return fun5();
})
.then(success)
.catch(errorhandler)
.finally(final);
and here is my all functions signature in service
var funX = function(param) {
var d = $q.defer();
d.notify('start with funX'); // Note: it was not working so placed inside else
doSomethingASync(param, function(err,data) {
if(err) { d.reject(err);}
else { d.notify('done with funX'); d.resolve(data); }
});
return d.promise;
});
Now my question is where do I receive this d.notify() message in my controller?
trial 1
.then(resp1, info1){
return fun2(resp1.id);
});
but it's undefined
trial 2
.then(resp1, err1, info1) {
return fun2(resp1.id);
}
but still undefined?
UPDATE
I have find a way by adding second parameter in finally()
.then().catch().finally(final, notify);
and here is my function definitions.
var errorHandler = function(err) {
console.error('Error returned from function:', err);
};
var final = function() {
console.log('Called Finally');
};
var notify = function(notification) {
console.log('Notify', notification);
};
var success = function(data) {
console.log('Success data');
console.log(data);
};
Can we get each promise function notification or this is not feasible?
But Now my query changed to
How do we add a .notify for the $q.all() ?
as I understand that $q.all returns a single promise which contains all promise resolve data;

wait for async http requests before proceeding angular

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);
});
});
};

Get resolved data from service within a service

How do I resolve the $promise generated by apiResource inside getDB() before it's actually returned back to my controller? Right now I get undefined.
people.factory('uniqueContacts', ['apiResource', function(apiResource) {
return{
getDB: function () {
apiResource.query({api_resource:'people'}).$promise.then(function(response){
return response.data
});
}
}
}]);
$resolvedData = uniqueContacts.getDB();
console.log($resolvedData);
There are 2 things:
first your function needs to actually return something
second here you have an async process so you'll need a callback, you cannot simply store the return in a variable like that
Check that code instead:
people.factory('uniqueContacts', ['apiResource', function(apiResource) {
return {
getDB: function () {
return apiResource.query({api_resource:'people'}).$promise.then(function(response){
return response.data
});
}
};
}]);
uniqueContacts.getDB().then(function (data) {
console.log(data);
});
Probably something like that. I made async the function getDB.
people.factory('uniqueContacts', ['apiResource','$q', function(apiResource,$q) {
return{
getDB: function () {
var d = $q.defer
apiResource.query({api_resource:'people'}).then(function(response){
d.resolve(response);
},function(err){
d.reject(err);
});
}
}
}]);
uniqueContacts.getDB().then(function(response){
$resolvedData = response;
console.log($resolvedData);
});
If you want to wait for the async call to be finished, add a promise table and wait for $q.all like that.
var promises = [];
promise.push(uniqueContacts.getDB().then(function(response){
$resolvedData = response;
}));
$q.all(promises).then(function(){
console.log($resolvedData);
});

a service to provide Restangular objects

I'm trying to make a service that will load persons from the server on demand. The first version looked like this:
services.factory('PersonServiceOld', function(Restangular, ErrorService) {
var persons = [];
var requesting = [];
var get = function(id) {
if (requesting[id]) {
return persons[id];
}
requesting[id] = true;
persons[id] = {'id' : id, 'photoName' : '0.png'};
Restangular.one('persons', id).get().then(function(success) {
persons[id].firstName = success.firstName;
persons[id].lastName = success.lastName;
persons[id].photoName = success.photoName;
}, function(failure) {
requesting[id] = false;
ErrorService.serverError(failure);
});
return persons[id];
};
var reset = function() {
persons = [];
requesting = [];
};
return {
getPerson : get,
clearCache : reset,
};
});
That way I get a reference to an object right away and it will be filled with data slightly after. It worked well... until I noticed that in another use case, I also want to request the address of a person like
var person = PersonService.get(id);
person.one(address).get().then(.......
but the objects returned from my PersonService aren't Restangular objects. So I tried something else:
services.factory('PersonService', function(Restangular, ErrorService) {
var persons = [];
var get = function(id) {
if (!persons[id]) {
persons[id] = Restangular.one('persons', id);
persons[id].get().then(function(success) {
}, function(failure) {
ErrorService.serverError(failure);
persons[id] = null;
});
}
return persons[id]; // also tried: persons[id].$object
};
return {
getPerson : get
};
});
I hope somebody understands what I'm trying to do here and can give me a good pointer on how to achieve this.
Check this Plunkr for a complete example.
As Restangular returns promises, and your get function may be asynchronous or synchronous (in case you use your own cache), you need to create a promise for returning always the same type of object.
You can do it as described in the Angular documentation for $q service.
So your get function may look like :
var get = function (id) {
var deferred = $q.defer();
if (store[id]) {
deferred.resolve(store[id]);
} else {
Restangular.one('person', id).get().then(function (res) {
store[res.id] = res;
deferred.resolve(res);
}, function (err) {
deferred.reject(err);
});
}
return deferred.promise;
}
Then in your controller, for retrieving your data :
PersonService.get(475).then(function (person) {
// stuff
}, function (err) {
// err handling
});

Resources