time

AngularJS $http and $resource

AngularJS has two services that wrap the native browser XMLHttpRequest class: $http and $resource. The $http service is comparatively low level and exposes a request and response abstraction around HTTP calls. The $resource service is more high level and provides an object-level abstraction—that is, loading and saving an object from the server as opposed to making requests directly. Both interact with the server via HTTP.

So, you can send AJAX requests in several different ways

  • AJAX calls via the $http service;
  • JSONP calls via the $http service;
  • REST type calls.

$http examples

$http is an AngularJS service for reading data from remote servers.

$http.get(url) is the function to use for reading server data.

<div ng-app="TestApp" ng-controller="TestCtrl">

<ul>
  <li ng-repeat="man in men">
    {{ man.name + ', ' + man.age + ', ' + man.height }}
  </li>
</ul>

</div>

<script>
var app = angular.module('TestApp', []);
app.controller('TestCtrl', function($scope, $http) {
    var config = {
        params: {age: 22, height: 180}
    };

    $http.get("/remote/url/", config).success(function(data, status, headers, config) {
        $scope.men = data;
    }).error(function(data, status, headers, config) {
      // log error
    });
});
</script>

The $http.get() function returns a "promise" object. This promise object has a success() and an error() function. By calling these functions and pass a function to them as parameter you can control what happens when the AJAX call finishes.

The $http service has several functions you can use to send AJAX requests. These are:

  • $http.get(url, config)
  • $http.post(url, data, config)
  • $http.put(url, data, config)
  • $http.delete(url, config)
  • $http.head(url, config)

Notice that the $http.post() and $http.put() functions take a data parameter which contains data to be sent to the server. The rest of the $http functions cannot take a data parameter.

Post (create) object to server

var params = {name: 'John', age: 28, height: 180};
$http.post("/remote/url/", params).success(function(data, status, headers, config) {
    $scope.men.push(data.man);
    $scope.message = data.msg;
}).error(function(data, status, headers, config) {
    alert( "failure message: " + JSON.stringify({data: data}));
});        

Put (update) object to the server

$scope.updateMan = function(){
    $http.put('/romote/url/' + $scope.man.id, {"men": $scope.man}).success(function(data, status, headers, config){
        $scope.man = data.man;
    }).error(function(response, status, headers, config){
        $scope.message = data.message;
    });
};

Delete object in the server

$scope.delMan = function(man) {
    $http.delete('/remote/url/' + man.id).success(function(data, status, headers, config){
        var index = $scope.men.indexOf(man);
        $scope.man.splice(index, 1);
    }).error(function(response, status, headers, config){
        $scope.message = data.msg;
    });
}

$resource examples

The $resource service is a high-level abstraction around $http that allows you to operate at the abstraction of objects loaded from the server rather than at the level of individual HTTP requests and responses.

Often our backend data is modelled in a way that naturally fits into the client-side data models in our app. For example, if our app has a list of Man that we show in a stream, a Man model becomes a core data element on both the server and the client.

Additionally, backend services that work on individual models like this often implement a RESTful API where we can easily create, read, update, and delete individual items, or grab lists of items.

The $resource service is not part of the AngularJS core. To use it, you must include the angular-resource.js file and add a dependency on the ngResource module.

The $resource function has the following signature. Note that square brackets mean the parameter is optional and may be omitted

$resource(url, [paramDefaults], [actions], options);

This function returns a resource object that has a set of functions called actions. Each action corresponds to a different class of HTTP requests.

For the sake of this example, let’s assume we have an API for Man. This API has a few endpoints supporting some common HTTP verbs:

  • GET /api/man - Lists all Man
  • POST /api/man - Creates a new Man
  • GET /api/man/:id - Returns the data for that specific Man with the id of :id
  • PUT /api/man/:id - Updates the information for a specific Man
  • DELETE /api/man/:id - Deletes the specific Man

With ngResource, we can easily wrap this API and interact with it:

var app = angular.module('TestApp', ['ngResource']);

app.factory('Man', function($resource) {
  return $resource('/api/man/:id');
});

app.controller('TestCtrl', function($scope, Man) {
  // Get all men
  $scope.men = Man.query();

  // Our form data for creating a new Man with ng-model
  $scope.postData = {};
  $scope.newMan = function() {
    var man = new Man($scope.postData);
    man.$save();
  }

  // Get specific Man
  Man.get({id: 1}, function(data) {
    $scope.man = data;
  });  
});

And then we can easily have a form for creating new Man

<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.18/angular-resource.min.js"></script>
<body ng-app="TestApp" ng-controller="TestCtrl">
    <form ng-submit="newMan()">
        <label>New man:</label>
        <input ng-model="postData.name" placeholder="Name">
        <input ng-model="postData.age" placeholder="Age">
        <input ng-model="postData.height" placeholder="Height">
        <button type="submit" class="button button-positive">Create</button>
    </form>
</body>

When the user submits this form, the newMan() function is called, creating a new instance of our Man resource, and then calling $save() which does an HTTP POST to /api/man.

We can create a new Man using save

Man.save(data)

We can update the existing Man using $save

var man = Man.get({id: 1}, function() {
    man.name = "Mark";
    man.$save();
});

And we can delete a specific Man by id

Man.delete({id: id});

What if your response of Men is not an array but a more complex json? This typically results in the following error TypeError: Object #Resource has no method 'push'. Angular seems to expect your service to return a JSON array.

In this case you have to change the $resource definition accordingly.

app.factory("Man", function($resource) {
  return $resource("/api/man/:id", {}, {
    query: { method: "GET", isArray: false }
  });
});

app.controller("TestCtrl", function($scope, Man) {
  Man.query(function(data) {
    $scope.men = data.men;
  });
});

Tweak $resource

Transform data after request

app.factory('Man', function($resource) {
    return $resource('/api/man/:id', null, {
        query: {
            method: 'GET',
            isArray: true,
            transformResponse: function(data, headersGetter) {
                var items = angular.fromJson(data);
                console.log(items);
                return items;
            }
        }
    });
});

Do some work after request

var men = Man.query({}, function(res){console.log(res)})
// or 
var men = Man.query().$promise.then(function(res){console.log(res)});

Useful links

comments powered by Disqus