andro1d
andro1d

Reputation: 568

Abstracting $http calls into service

I'm wondering what the best way to abstract $http calls into an angularjs service is. I've done a bit of research and this seems to be the most common way:

app.factory('myService', function($http) {
 return {
   getFoo: function() {
     return $http.get('foo.json').then(function(result) {
       return result.data;
     });
   }
 }
});

app.controller('MainCtrl', function($scope, myService) {
  //the clean and simple way
  $scope.foo = myService.getFoo();
}

But the problem with this approach is I can't figure out how to do anything on .error.

I'd prefer to have my .success and .error callbacks inside my controller.

Is there a way to abstract the http call inside the service whilst maintaining .error and .success callbacks inside the controller?

Thank you.

Upvotes: 4

Views: 617

Answers (1)

Clark Pan
Clark Pan

Reputation: 6027

You can still use the on success/error calls.

The method you've highlighted returns a "Promise" object. A good thing about promises is that they are chainable.

So say you wish to respond to a $http request error in your controller:

app.factory('myService', function($http) {
 return {
   getFoo: function() {
     return $http.get('foo.json').then(function(result) {
       return result.data;
     });
   }
 }
});

app.controller('MainCtrl', function($scope, myService) {
  //the clean and simple way
  $scope.foo = myService.getFoo().then(function(){
    //Do something with successful response
  }, function(){
    //Do something with unsuccessful response
  });
}

NOTE: This following next section no longer holds true. Promises used in templates are no longer automatically resolved to its values when the promise resolves.

You should also understand why assigning $scope.foo works in your templates. AngularJS has a bit of magic that will resolve any promises to the object you need in a template. So while your template might reference foo.bar and the output will be correct, whats actually happening in the background is that the template is waiting for the promise to be fulfilled before rendering that part of the template.

Also, another gotcha is to remember to return an rejected promise if you're handling the error somewhere up in the chain.

For example:

app.factory('myService', function($http, $q) {
 return {
   getFoo: function() {
     return $http.get('foo.json').then(function(result) {
       return result.data;
     }, function(result){
      //I'm doing something here to handle the error
      return $q.reject(result);
     });
   }
 }
});

app.controller('MainCtrl', function($scope, myService) {
  //the clean and simple way
  $scope.foo = myService.getFoo().then(function(){
    //Do something with successful response
  }, function(){
    //Do something with unsuccessful response
  });
}

If we didn't return a rejected promise in the service, the controller's 'success' code path will be run instead of the reject path.

Upvotes: 4

Related Questions