At first sight AngularJS seems like a magical framework, which does some weird but awesome things like dependency injection, data binding only by setting a single property to the magical object named $scope and many other things.
If you look at the source code of Angular you’ll see a lot of smart and simple solutions for these magical problems. In this blog post I’ll describe the magic which stays behind
$resource. I decided to write this blog post because of my recent experience in StackOverflow. One of the most misunderstood components of AngularJS was exactly the
$resource is two levels of abstraction above the XMLHttpRequest object (ok, may be three if we count
$httpBackend). I’ve illustrated the usage of
$resource through example which can be found at GitHub.
Before continuing with
$resource I’m going to explain few important concepts. The first one is:
This is architectural pattern named by Martin Fowler. The Active Record object is an object, which carries both data and behavior. Usually most of the data in these objects is persistent, responsibility of the Active Record object is to take care of the communication with the database in order to create, update, retrieve or delete the data. It may delegate this responsibility to lower level classes but calls to instance or static methods of the active record object cause the database communication.
In the example above we have
User constructor function, which also has methods
save. We can use it by:
Lexical functional scope
$resource. As you see in the
save method, in example above, we keep reference to
self. This reference is visible in the callback we pass to the promise returned by
db.process. When the promise is resolved we just populate the missing data (in this case
id) and return the object.
We can do something similar in
In this case when
User.query() is called it immediately returns an empty array with property
$promise. When the promise is resolved (i.e. database request successfully completed), we just push each of the users into our array
users. Note that the user of our code already has reference to this variable, so when we populate it he will be able to use all the data immediately.
The last thing we will look at before continuing with
$resource is AngularJS’s
$digest loop. Probably you’re aware that when AngularJS finds some “special” directives in our templates it automatically registers watchers for the expressions used in these directives. Such “special” directives are ``,
ng-bind. When something cause call of the digest loop AngularJS iterates over all watched expressions and calculates their values until there are no more changes (when we have wider support of
Object.observe, probably the
$digest loop will be more smarter and efficient).
So if we have:
with the following markup:
Now if somewhere else, something keeps the reference to the
users array it can simply:
which will cause rendering of a single list item with the value
We won’t cover the whole API of
$resource but we will look at how we can implement our model (not view model) with it. First of all, lets suppose we have a nice RESTful API for creating, retrieving, updating and deleting users:
Now we can create our model by:
$resource will create a constructor function for our model instances. Each of the model instances will have methods, which could be used for the different CRUD operation…and that’s all?! We implemented the communication with the back-end by invoking
$resource with a single parameter!
But how about custom actions like
login, for example? The
$resource API allows us to include them explicitly.
I would recommend you to wrap the constructor function produced by
$resource in a service. You will be able to reuse it across the application, mock it easier when writing tests and extend its functionality without modifying additional code (we follow the open-closed principle):
Now let’s get all users:
And now visualize all users…:
You might think that the operation
query is synchronous because of the way we set the value of
$scope.users. This will be very bad, right? Well, it is asynchronous,
MainCtrl simply uses two of the concepts we discussed above: functional lexical scope,
query method is invoked it returns a reference to an empty array, the array has just a single property called
$promise, you can use it if you want to manipulate the users after the request was successful, or not. When the request completes AngularJS populates the array with all users and calls the
$digest loop. The
query action will make a GET request to
There is something cool in AngularJS’s route definition, which allows us to change the current page once all dependencies of the next page are resolved – the
resolve hash. This feature is very helpful especially if we want to open a page with single user view:
As I mentioned above, when we invoke “static” method of the constructor function, it returns an empty object with property
$promise, which will be resolved once the request is completed. This way we make sure the user will see the page once the user with id
$route.current.params.userid is available.
And this is our
You may noticed the
setTimeout which is added in the express web app. It cause slower response, only to illustrate how useful could be our next enhancement – cache. We can cache some requests by passing additional parameters to the
$resource function. For example:
The example above will make each call of the
get method cachable (it won’t affect the
query method). After adding this line you’ll notice that, after the first time, on each subsequent click on somebody’s name you’ll be immediately redirected to the user’s view, you won’t have to wait the request to complete because AngularJS does not make any requests – it takes the value from the cache.
Most of these methods also accept callbacks, but I would recommend you to stick to the promisee usage.
Once again – you can grab the sample application here.
$resource allows us to use Active Record like pattern of communication with our RESTful service. It will definitely save you a lot of lines of code, if your back-end provides proper API. It gives you features like caching, interceptors, specifying response type and timeout, it even provides flag “with credentials“.
However, when you have more complex/custom back-end API, you may consider to implement all the communication by your own since, otherwise, you’ll need to override most of the methods of