So you figured that since Angular is an amazing front-end MVC (or MV-*whatever*) and Sails is an ever better back-end framework, that mixing the two yields success. Well, you’re right – at least sort of. The most difficult thing is wrapping your head around how two separate MVC’s that communicate over an API.

Let’s start with an example in which we want to find the total number of bottles in our inventory.

What exactly happens between when we click a button, labeled “Total Amount”, and when we receive the exact amount?

Firstly, the view’s button has an ng-click linking it to the related controller. From there, the controller calls the service which jumps to /bottle/totalAmount. This is where Sails comes in; Sails has an amazing REST API to simplify routing and data management. Although Sails doesn’t support the method totalAmount, we (coding ninjas) do. In Sails, we use the controller to communicate with the service who will lastly access the model. It might seem a bit winded, however it greatly modularized the code.

To save time, lets pretend that an ng-click is bound to an HTML button in the view called totalAmount(), which in the controller, makes a call to its related service:

1:   /** Load Total Bottles **/
        2:   var totalBottlePromise = BottleService.totalAmount({});
        3:   totalBottlePromise.then(function(response){
        4:    $scope.totalBottles = response;
        5:   }, function(reason){
        6:    throw reason;
        7:   });
        

In order for the view to access the back-end model we utilize Angular’s service to make an HTTP get to the bottle Model. It would look something like this:

1:  return {
        2:    'get': function(params) {
        3:     var defer = $q.defer();
        4:     if(params != undefined){
        5:      $http.get('/bottle/totalAmount' + '?' + $.param(params))
        6:      .success(function(resp){
        7:       defer.resolve(resp);
        8:      }).error( function(err) {
        9:       defer.reject(err);
        10:      });
        11:     } else{
        12:      $http.get('/bottle/totalAmount').success(function(resp){
        13:       defer.resolve(resp);
        14:      }).error( function(err) {
        15:       defer.reject(err);
        16:      });
        17:     }
        18:     return defer.promise;
        19:    }
        20:   };
        

I added $q because we want an asynchronous call.

Now we’re in Sails. Since Sails automatically adds routes for controllers via REST, we then need add a method totalAmount which will then call our service.

1:   module.exports = {
        2:    totalAmount: function(req, res) {
        3:      BottleService.totalAmount(function(err, data){
        4:        res.json({totalAmount: data});
        5:      });
        6:    }
        7:   };
        

Great, so we expect the service to return an object with totalAmount as the key and “data” as the value. Let’s move on to the service.

1:   module.exports = {
        2:    totalAmount: function(next) {
        3:     Bottle.count(function (err, count) {
        4:      next(err, count);
        5:     });
        6:    }
        7:   };
        

Final Thoughts

Just to recap – view -> controller -> service -> | API | -> controller -> service -> model, then all the way back up-stream.

Phew. That might seem like a lot, but understand how both frameworks relay information is the crux of taking full advantage of a completely RESTful application.