You wish to consume a RESTful data source.
Use Angular’s high-level $resource
service. Note that the Angular ngResource
module needs to be separately loaded since it is not included in the base angular.js file:
<script src="angular-resource.js">
Let us now start by defining the application module and our Post
model as an Angular service:
var app = angular.module('myApp', ['ngResource']);
app.factory("Post", function($resource) {
return $resource("/api/posts/:id");
});
Now we can use our service to retrieve a list of posts inside a controller (example: HTTP GET /api/posts):
app.controller("PostIndexCtrl", function($scope, Post) {
Post.query(function(data) {
$scope.posts = data;
});
});
Or a specific post by id
(example: HTTP GET /api/posts/1):
app.controller("PostShowCtrl", function($scope, Post) {
Post.get({ id: 1 }, function(data) {
$scope.post = data;
});
});
We can create a new post using save (example: HTTP POST /api/posts):
Post.save(data);
And we can delete a specific post by id
(example: DELETE /api/posts/1):
Post.delete({ id: id });
The complete example code is based on Brian Ford’s angular-express-seed and uses the Express framework.
You can find the complete example on github.
Following some conventions simplifies our code quite a bit. We define the $resource
by passing the URL schema only. This gives us a handful of nice methods including query
, get
, save
, remove
and delete
to work with our resource. In the example above we implement several controllers to cover the typical use cases. The get
and query
methods expect three arguments, the request parameters, the success and the error callback. The save
method expects four arguments, the request parameters, the POST data, the success and the error callback.
The $resource
service currently does not support promises and therefore has a distinctly different interface to the $http
service. But we don’t have to wait very long for it, since work has already started in the 1.1 development branch to introduce promise support for the $resource
service!
The returned object of a $resource
query or get function is a $resource
instance which provides $save
, $remove
and $delete
methods. This allows you to easily fetch a resource and update it as in the following example:
var post = Post.get({ id: 1 }, function() {
post.title = "My new title";
post.$save();
});
It is important to notice that the get
call immediately returns an empty reference - in our case the post
variable. Once the data is returned from the server the existing reference is populated and we can change our post title and use the $save
method conveniently.
Note that having an empty reference means that our post will not be rendered in the template. Once the data is returned though, the view automatically re-renders itself showing the new data.
What if your response of posts 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. Have a look at the following JSON example, which wraps a posts
array in a JSON object:
{
"posts": [
{
"id" : 1,
"title" : "title 1"
},
{
"id": 2,
"title" : "title 2"
}
]
}
In this case you have to change the $resource
definition accordingly.
app.factory("Post", function($resource) {
return $resource("/api/posts/:id", {}, {
query: { method: "GET", isArray: false }
});
});
app.controller("PostIndexCtrl", function($scope, Post) {
Post.query(function(data) {
$scope.posts = data.posts;
});
});
We only change the configuration of the query
action to not expect an array by setting the isArray
attribute to false
. Then in our controller we can directly access data.posts
.
It is generally good practice to encapsulate your model and $resource
usage in an Angular service module and inject that in your controller. This way you can easily reuse the same model in different controllers and test it more easily.