105

I have a single factory defined with ngResource:

App.factory('Account', function($resource) {
    return $resource('url', {}, {
        query: { method: 'GET' }
    });
});

I am making multiple calls to the query method defined on this factory. The calls can happen asynchronously, but I need to wait for both calls to complete before continuing:

App.controller('AccountsCtrl', function ($scope, Account) {
    $scope.loadAccounts = function () {
        var billingAccounts = Account.query({ type: 'billing' });
        var shippingAccounts = Account.query({ type: 'shipping' });

        // wait for both calls to complete before returning
    };
});

Is there a way to do this with AngularJS factories defined with ngResource, similar to jQuery's $.when().then() functionality? I would prefer not to add jQuery to my current project.

3 Answers 3

205

You'll want to use promises and $q.all().

Basically, you can use it to wrap all of your $resource or $http calls because they return promises.

function doQuery(type) {
   var d = $q.defer();
   var result = Account.query({ type: type }, function() {
        d.resolve(result);
   });
   return d.promise;
}

$q.all([
   doQuery('billing'),
   doQuery('shipping')
]).then(function(data) {
   var billingAccounts = data[0];
   var shippingAccounts = data[1];

   //TODO: something...
});
Sign up to request clarification or add additional context in comments.

8 Comments

Resources don't return promises, they return objects to be filled in the future. However in the unstable 1.1.3 version, resources also have $then property but do not expose any promise object. Exposing $promise completely would be in 1.1.4
@UmurKontacı This is unfortunately not in angular 1.1.4!
Details about the resources are not promises problem can be found in this thread and in this pull request.
This answer shows how to write it once that is implemented.
Your answer is very helpful and I believe it is the most sensible way to convert resources to promises in the current angular. It might be helpful to add that in the documentation of $q, which you linked to, it guarantees that the result array is in the same order as the promise array.
|
21

I think a better solution is:

$q.all([
   Account.query({ type: 'billing' }).$promise,
   Account.query({ type: 'shipping' }).$promise
]).then(function(data) {
   var billingAccounts = data[0];
   var shippingAccounts = data[1];

   //TODO: something...
});

1 Comment

For me worked without $promise at the end... Just like: Account.query({ type: 'billing' }), Account.query({ type: 'shipping' })
13

The solution from Ben Lesh is the best but it's not complete. If you need to handle error conditions--and, yes, you do--then you must use the catch method on the promise API like this:

$q.all([
   doQuery('billing'),
   doQuery('shipping')
]).then(function(data) {
   var billingAccounts = data[0];
   var shippingAccounts = data[1];

   //TODO: something...

}).catch(function(data) {

   //TODO: handle the error conditions...

}).finally(function () {

  //TODO: do final clean up work, etc...

});

If you don't define catch and all of your promises fail, then the then method won't ever execute and thus will probably leave your interface in a bad state.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.