1

First project in AngularJS and I started creating my services (factories) that I made modular like this

angular.module('app.services.public', [])
    .factory('publicService', ['$http', function publicService($http) {
        var results = {};
        results.contact = function (name, email, message){
             return $http.get();
        };
        return results;
    }]);

That I then call in my main angular app by including it. When I call it, I need to listen for success or error

publicService.contact().success(callback).error(callback)

My question is, I'm going to be doing a lot of API requests through these services and seems to be bad code to listen to the error everytime since 90% of the time it will do the same thing.

How can I create a wrapper around the $http.get or around all factory calls?

So something like

apiCall = function (url, data, successCallback, errorCallback){
    $http.get(url,data).success(function(){
        successCallback()
    }).error(function(){
        if(errorCallback()){ errorCallback(); return; }
         // or display general error message
    })
}
denislexic
  • 10,786
  • 23
  • 84
  • 128

3 Answers3

1

You have the right idea. You can do it easily with a Factory.

myApp.factory(APIService, function(publicService, $http) {
  return {

    // create methods in here
    ...

    contact: function(cb) {
      $http.get(url,data).success(cb).error(function(err){
        console.error('oh no!', err);
      });
    }
  };
});

Then you can use it in your controllers.

APIService.contact(function(data){
  console.log('response from the api!', data);
});

You can even move your error handler to its own factory as well.

mz3
  • 1,314
  • 11
  • 27
1

I would recommend against converting promise-based into callback-based APIs. Angular adopted promises and it best to stay with them. Also, stay away from $http-specific .success/.error and use promise .then/.catch APIs.

How wide do you need to cast your net to handle $http errors?

1) Say, it only applies to your publicService service, then you can "handle" it at the each function:

.factory("publicService", function($http, $q){ 
    function handleError(){
      // invokes error handlers
    }
    return {

      onError: function(cb){
        // register error handlers
      },
      doSomethingA: function(){
        return $http.get("some/url/A")
                    .then(function(response){
                       return response.data;
                    })
                    .catch(function(error){
                       handleError(error);
                       return $q.reject(error); // still "rethrow" the error
                    }
      },
      doSomethingB: function(){
        // similar to above
      },
      // etc...
    };

})

Then you could separate request from error handling:

.controller("MainCtrl", function($scope, publicService){
   publicService.onError(function(error){
     $scope.showError = true; // or something like that
   })
})
.controller("FunctionACtrl", function($scope, publicService){
   publicService.doSomethingA()
                .then(function(data){
                  $scope.data = data;
                });
})

2) Of course, the above, would only apply to request made via publicService. If you want to catch all $http errors, you could implement an $http interceptors. I won't go into detail - there is enough info in documentation and elsewhere - but it would could work like below:

.factory("ErrorService", function(){
  return {
    onError: function(cb){
      // register error handlers
    },
    broadcastError: function(error){
      // invoke error handlers
    }
  };
})

Then in interceptor, use ErrorService as a dependency:

'responseError': function(rejection) {

   ErrorService.broadcastError(rejection);

   return $q.reject(rejection);
}

Then you could handle the errors globally:

.controller("MainCtrl", function($scope, ErrorService){
   ErrorService.onError(function(error){
     $scope.showError = true; // or something like that
   })
})
New Dev
  • 48,427
  • 12
  • 87
  • 129
  • Would be really helpful if you could develop a bit more on the second method. – denislexic Jul 15 '15 at 20:39
  • 1
    @denislexic, there is some boilerplate code around creating interceptors - see [here](http://stackoverflow.com/a/29221969/968155) and [here](http://stackoverflow.com/a/30383841/968155) for more context – New Dev Jul 15 '15 at 20:45
  • For reference to other people, this article is pretty good on explaining how it all works: http://www.webdeveasy.com/interceptors-in-angularjs-and-useful-examples/ – denislexic Jul 16 '15 at 22:15
1

I would suggest an implementation using angular's $q service.

angular.module('app.services.public', [])
    .factory('publicService', ['$http', '$q', function publicService($http, $q) {
        var results = {};
        results.contact = function (name, email, message){
             return $q.when($http.get());
        };
        return results;
    }]);

Or rather than use the $q.when(...) method you can use $q.deferred like so:

angular.module('app.services.public', [])
        .factory('publicService', ['$http', '$q', function publicService($http, $q) {
            var deferred = $q.deferred();
            var results = {};
            results.contact = function (name, email, message){
                 $http.get().success(function(data){
                      deferred.resolve({
                         // assumes data retried from http request has a title and price attribute
                         title: data.title,
                         cost: data.price});
                 }).error(function(data){
                       deferred.reject(data);
                 });
            };
            return deferred.promise;
        }]);
alacy
  • 4,972
  • 8
  • 30
  • 47