Monday, June 9, 2014

AngularJS: Adding new objects with Restangular

Restangular allowed us to define models for our application that are automatically generated for us as we load the data from our API. However, we ran into a small problem when posting new objects in our app as these were not originally loaded through Restangular, and thus, were just plain old JavaScript objects, and not objects created through our model. Lets take a look!

The problem

Imagine our app to be a typical users CRUD page where we show a list of users - and we can add, edit, and remove these users. The API has a users endpoint that provides us the basic CRUD operations that we need.

Let's say that as part of our model we want to compile the full name of the user. So we add a fullName method to our model:

Restangular.extendModel('users', function(model){

 model.fullName = function(){
  return this.firstName + this.lastName;
 };

 return model;
});

So far so good. We can load our users, Restangular will automatically add a fullName method to each user object. Let's say we are displaying these users in a simple html view by setting the users property in our scope.

<div ng-repeat="user in users">
 <span>{{user.fullName()}}</span>
</div>

Nice! This works as expected, but what happens when we would like to add a new user. Normally, we could simply add a plain old object to the collection of users, but what about our new fullName method? We need to make sure that as we dynamically add new users, we somehow provide to each user object, all the methods defined in our model.


Restangular.all('users').getList().then(function(users){
 $scope.users = users;
});

...

var newUser = { firstName: 'first', lastName: 'last' };
$scope.users.push(newUser);

$scope.users[$scope.users.length - 1].fullName() // Error - this object does not have a method called 'fullname'

The solution

Luckily, Restangular has the flexibility to allow us to do just that. Let's take a look at the code

Restangular.extendCollection('users', function(collection){
                                                … 
  collection.add = function(userData){
    var user = Restangular.restangularizeElement(this.parentResource, buildData, 'users');
    this.push(user);
  };
 
  return collection;
});

Apart from adding new method to our models, Restangular also provides us with the capability of adding new methods to our collection of models with the extendCollection method. This collection would be the result of calling the Restangular method getList to load a list of users. Please refer to the documentation for more information on loading data with Restangular.

This new add method uses the Restangular.restangularizeElement method that builds a model with the data we provide as if Restangular was loading the data for this user from an API call. This built model will have our fullName method. We can then proceed to add the new user object to the collection itself.


Restangular.all('users').getList().then(function(users){
 $scope.users = users;
});

...

var newUser = { firstName: 'first', lastName: 'Last' };
$scope.users.add(newUser);

$scope.users[$scope.users.length - 1].fullName() // 'First Last'

Conclusion

Admittedly, this is a very simple solution, for a simple problem, but I could not find any good documentation online on the recommended way to handle this particular case with Restangular. I hope this blog post proofs to be useful, and it saves somebody the need to dig through the Restangular documentation and/or code. Let me know if you have a simpler solution to this problem!