understanding the angular js $resource

$Resource Basic

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 $resourceservice. $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.

Basic flow:

1. resource resourceFactory function will parse all the actions

2. each action could have 4 params: {params, data, success, error}, if 3 param then {params, success, error}, if 2 param then {success, error}, if 1 param then {success}

3. a httpConfig will be constructed according to the params and then be applied to $http service.

4. when the $http call resolves, the response will be decorated and the returned response will have a ‘reousrce‘ with value of a object with $resovled and $promise so that the later thens could use.

5. there is a 1st internal then which will execute the success/error function passed in from the function definition[ie: Resource.SomeMethod(params, success, error) ]. This then will return a responseInterceptor(response) which basically just returns the response.resource mentioned above so the following chaining thens could use.

More about $q and promise, read the other post.

Before continuing with $resource I’m going to explain few important concepts. The first one is:

Active Record

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.

For example:

function User(name, age) {
  this.id = null;
  this.name = name;
  this.age = age;
}

User.prototype.save = function () {
  var self = this;
  return db.process('INSERT INTO User (name, age) VALUES (' + this.name + ', ' + this.age + ')')
    .then(function (user) {
      self.id = user.id;
      return self;
    });
};

User.query = function () {
   return db.process('SELECT * FROM User');
};

In the example above we have User constructor function, which also has methods query and save. We can use it by:

var user = new User('foo', 42);
user.save()
.then(function () {
  //user.id is now available
});

User.query()
.then(function () {
  //all users
});

In the client-side JavaScript everything is a little bit different but the concepts are the same. What are the problems of this pattern? Well, in the relational databases we work with relations, which are represented as tables. We don’t have the standard OO concepts such as inheritance or association. Of course, we can emulate them by using tables but this leads to a problem – we don’t have 1:1 mapping between our tables and classes. Thats why sometimes for ORM is preferred the pattern Data Mapper, but topic is out of the scope of the current post. Fortunately, in the Single-Page Applications, usually we have back-end, which probably uses ORM framework and provides us a nice JSON API, so we don’t have to worry about complex mapping, we can just use client-side Active Record.

Lexical functional scope

This is very basic JavaScript concept but also one used in $resource. As you see in the save method, in example above, we keep reference tothis in 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 query

User.query = function () {
   var users = [];
   users.$promise = db.process('SELECT * FROM User')
     .then(function (collection) {
        collection.forEach(function (user) {
          users.push(user);
        });
      });
   return users;
};

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.

$digest loop

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-model, 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:

function MainCtrl($scope) {
  $scope.users = [];
}

with the following markup:

<ul ng-controller="MainCtrl">
  <li ng-repeat="user in users"></li>
</ul>

Now if somewhere else, something keeps the reference to the usersarray it can simply:

$scope.$apply(function () {
  users.push({
    name: 'foo'
  });
});

which will cause rendering of a single list item with the value "foo".

 

FROM HERE AND the documentation for $resource

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s