42

I would like to understand the advantages and disadvantages of using a simple $http request to a server and/or wrapping that request in a service versus using a ngResource object (other than the obvious regarding a RESTful resource).

From my understanding the $http requests are low level but very flexible and configurable whereas when dealing with a RESTful API the ngResource objects make communication very simple.

I guess what I am enquiring about is given a very simple scenario, say retrieval of data from a server (GET request of array of objects say) is it more efficient to simply use a $http request as opposed to either wrapping it in a service (should this always be the case?) or using an ngResource object?

Any thoughts here would be appreciated. For example, a $http response can be cached, but can an ngResource?

ggorlen
  • 44,755
  • 7
  • 76
  • 106
adamK
  • 3,849
  • 5
  • 37
  • 49
  • 3
    Both can be cached, you pointed out the reasons to use one over the other really in your question. If you have a RESTful interface then using $resource is better since you'll end up writing less boiler-plate code that is common to a RESTful interface, if you're not using a RESTful service then $http makes more sense. You can cache data either way http://pseudobry.com/power-up-%24http.html If this answers your question let me know and I'll post it as an answer and remove the comment. – shaunhusain Jul 16 '13 at 04:04
  • 1
    Thanks for the response, I guess it does, I was just wondering what the dis/advantages of $http versus ngResource would be, particularly in a fringe case as mentioned above where you are still returning objects, possibly from a RESTful API but require no further interaction other than GET. Perhaps I am reading to much into it, just couldn't find use cases discussed, only implementation. Also there has been some discussion suggesting $http requests should be wrapped in a service but found no definitive best practice. – adamK Jul 16 '13 at 04:57
  • 3
    I think putting $http requests into a service just generally works out better because you want to have access to the data from multiple locations and the service acts as a singleton so basically you can handle any kind of caching you want to do there and controllers can all just watch the appropriate services to update their own data. I've found that a combo of $watch in the controllers for data on the service and returning the promises from my service's methods gives me the most flexibility with how to update things in the controller. – shaunhusain Jul 16 '13 at 05:03
  • No problem glad it makes sense, really watch the video I linked below too though it's invaluable. – shaunhusain Jul 16 '13 at 05:22
  • You're not wrong, very informative! Cheers for that. – adamK Jul 16 '13 at 06:40
  • @shaunhusain how to get to know whether I have RESTful service or not ? – Tomasz Waszczyk Feb 12 '16 at 11:42
  • @TomaszWaszczyk-PantaRhei typically if it's a third party thing they will advertise it as "a RESTful API". Really this https://en.wikipedia.org/wiki/Representational_state_transfer#Architectural_constraints describes it well. The basic concept is you have resources and you can use HTTP Verbs (part of the request headers) like POST, PUT, GET, and DELETE on an endpoint and it will create, update, read, or delete a resource. For example if you are listing Movies you may use the URL http://api.domain.com/movie if you use a POST method on a request to that URL it would create a new Movie. – shaunhusain Feb 13 '16 at 02:01

2 Answers2

42

Decided I'll formulate this into an answer since in the comments we worked out basically what you wanted to know:

Using $http or $resource the results can still be cached, you pointed out the reasons to use one over the other really in your question. If you have a RESTful interface then using $resource is better since you'll end up writing less boiler-plate code that is common to a RESTful interface, if you're not using a RESTful service then $http makes more sense. You can cache data either way http://www.pseudobry.com/power-up-http-with-caching/

I think putting $http or $resource requests into a service just generally works out better because you want to have access to the data from multiple locations and the service acts as a singleton. So, basically you can handle any kind of caching you want to do there and controllers can all just watch the appropriate services to update their own data. I've found that a combo of $watch in the controllers for data on the service and returning the promises from my service's methods gives me the most flexibility with how to update things in the controller.

I'd put something like this in my controller having the exampleService injected at the top of the controller definition.

angular.module("exampleApp", []).service('exampleService', ["$http", "$q" ,function ($http, $q) {
    var service = {
        returnedData: [],
        dataLoaded:{},
        getData = function(forceRefresh)
        {
            var deferred = $q.defer();

            if(!service.dataLoaded.genericData || forceRefresh)
            {
                $http.get("php/getSomeData.php").success(function(data){
                    //service.returnedData = data;
                    //As Mark mentions in the comments below the line above could be replaced by
                    angular.copy(data, service.returnedData);
                    //if the intention of the watch is just to update the data
                    //in which case the watch is unnecessary and data can
                    //be passed directly from the service to the controller
                    service.dataLoaded.genericData = true;
                    deferred.resolve(service.returnedData);
                });
            }
            else
            {
                deferred.resolve(service.returnedData);
            }

            return deferred.promise;
        },
        addSomeData:function(someDataToAdd)
        {
            $http.post("php/addSomeData.php", someDataToAdd).success(function(data){
                service.getData(true);
            });
        }
    };
    service.getData();
    return service;
}]).controller("ExampleCtrl", ["$scope", "exampleService", function($scope, exampleService){
  //$scope.$watch(function() {return exampleService.returnedData}, function(returnedData){
  //  $scope.myModel.someData = returnedData;
  //});
  //if not using angular.copy() in service just use watch above
  $scope.myModel.someData = exampleService.returnedData;
}]);

Also here's a nice video from the Angular team on Best Practices that I'm still re-watching and slowly absorbing over time.

http://www.youtube.com/watch?v=ZhfUv0spHCY

Specifically on services vs controllers: http://www.youtube.com/watch?v=ZhfUv0spHCY&t=26m41s

Xavi Montero
  • 9,239
  • 7
  • 57
  • 79
shaunhusain
  • 19,630
  • 4
  • 38
  • 51
  • 13
    +1. You can eliminate the `$watch` in your controller if you use `angular.copy(data, service.returnedData)` in your service. This way, you'll update the same array, rather than assigning a new one. For a working plunker, see http://stackoverflow.com/questions/17416599/should-services-expose-their-asynchronicity – Mark Rajcok Jul 16 '13 at 13:08
5

There is one far more meaningful difference than whether they can be cached or not.

Using a resource will remove the need to set up $watches on the service or the returned data. You will not have to work with promises at all either. Essentially, it eliminates the need to do any of what shaunhusain is doing above in his example.

A call to a resource method returns an empty instance of the structure associated with that resource, and you can and should bind to it directly. This same instance will fill with data at some later time. Since you have bound to the instance, when it fills in, your display will update automatically.

Resources can also provide an encapsulated means of transforming requests and responses of the services it provides, making it all invisible to the client of the resource.

Resources are like services on steroids.

Rodney P. Barbati
  • 1,883
  • 24
  • 18
  • Just noticed this answer. I disagree regarding a lot of this. The $watches are a moot point in either case if you just use angular.copy to replace the contents of an object rather than the object itself. A service allows you to define an injectable object that is a singleton, I don't really see it as anything more or less. A $resource is just an abstraction on top of $http to handle the RESTful things you would end up rewriting using $http over and over. Promises expose the async nature of the request and can be obtained with $promise on the resource as well (and are a good thing). – shaunhusain Sep 22 '14 at 19:55
  • I have scenarios where I receive various kinds of errors from the server - input or business validation error related to a form field or a general business error, or "This record has been edited by someone else, please reload it", or user session expires because of inactivity, or unexpected server error. Some of these errors return as HTTP 500 or 422 or 401 or in "error[key]", depending on the kind of error. Central $http wrapper makes it easy to process all these errors in one place. I'm not sure how I would process these errors if using $resource. – JustAMartin Aug 05 '15 at 10:31
  • @JustAMartin Even if you use $resource, internally it works on $http, so you can globally intercept all server request and response and can handle errors globally. – Mukun Dec 22 '15 at 03:40
  • @JustAMartin you don't have to sacrifice $resource, you can just do an HTTP request/response interceptor https://djds4rce.wordpress.com/2013/08/13/understanding-angular-http-interceptors/ – Toolkit Mar 02 '16 at 17:40
  • yes for me this seems like a better solution, i always hated services because i will have to setup watches and i preferred using rootScope in fact and the fact that I can assign resource directly is a benfit – Toolkit Mar 02 '16 at 17:41
  • Still don't understand why people are using $watches on service data. Bind to the unfilled, undefined variable you created in your controller and that is it. The form elements using the undefined value won't display until the data arrives, at which point the form will become visible. No need to watch for the data, double binding is already watching. – Rodney P. Barbati Sep 09 '16 at 23:42