Saturday 16 February 2013

Modules in Angular JS

Angular JS supports modules, using which we can divide the JavaScript code involved in our application. Creating modules not only helps separating the code into individual concerns but also improves unit-testability. The modules can be easily replaced by some mocks while unit testing Angular controllers.

Following is the syntax of creating and registering a module:

angular.module(‘module-name’,[dependencies]);

Every module should have a name, which is specified in the first parameter. Second parameter to module function is an array, which may contain names of other modules or services. If the module doesn’t depend on anything, the array can be left blank.

To a module, we can add services, filters, directives, constants, configuration blocks and run blocks. In large applications, it is recommended to add each of these to separate modules. A typical module with all of these blocks added looks like the following:

angular.module(‘module-name’,[dependencies])
 .value(‘service-name’,{
  //properties and functions to be added to the service
         }).
         filter(‘filter-name’,{
         //definition of filter
         }).
         directive(‘directive-name’,function(parameters){
         //directive definition
         }).
         constant(‘constant-name’,object).
         config(function(injectables){
         //configurations to be applied
         }).
         run(function(injectables){
         //code to kick-start the application
         });

In last two posts, we created a shopping cart with functionalities to add and remove items, calculate total price, sort and filter data present in the cart array. Since controller should play the role of mediator, let’s separate data of shopping cart and operations into a separate module and call these functions from controller wherever we need the functionality to be executed. Following is the module that holds data and performs required operations on the data:
angular.module('shopping',[])
   .value('shopping_cart_operations',{
    items : [
     {Name: "Soap", Price: "25", Quantity: "10"},
     {Name: "Shaving cream", Price: "50", Quantity: "15"},
     {Name: "Shampoo", Price: "100", Quantity: "5"}
     ],

    addItem: function(item){
     this.items.push(item);
    },

    totalPrice: function(){
     var total = 0;
     for(count=0;count<this.items.length;count++){
      total += this.items[count].Price*this.items[count].Quantity;
     }
     return total;
    },

    removeItem: function(index){
     this.items.splice(index,1);
    } 
   });

As controller requires the functions defined in the service shopping_cart_service, it has to be passed as a dependency to the controller. The dependency will be automatically resolved at the run-time from the module.

Angular makes heavy usage of Dependency Injection. As we have already discussed in first post, $scope is a mandatory parameter to the controller, but we didn’t discuss about from where it gets its behaviour. It is a service defined in the library and gets injected into the controller when we refer to it by its exact name. To refer it using some other name, we have to assign the modified names to $inject property in the same sequence in which they have to be passed into the function.

MyController.$inject = [‘$scope’, ‘any_other_service’];

After this, we can define the controller as follows:
function MyController(myScope,otherService){
 //Controller body
}
Here, myScope and otherService will hold the values of $scope and any_other_service respectively. To learn more on dependency injection on Angular JS, you can refer to the official website of Angular JS.

Let’s modify ShoppingCartCtrl to use the shopping module created above.

function ShoppingCartCtrl($scope,shopping_cart_operations)  {
 $scope.items = shopping_cart_operations.items;
 $scope.item = {};

 $scope.addItem = function(item) {
  shopping_cart_operations.addItem(item);
  $scope.item = {}; //clear out item object
 };
    
 $scope.totalPrice = shopping_cart_operations.totalPrice;
  
 $scope.removeItem = function(index){
  shopping_cart_operations.removeItem(index);
 };
  
 $scope.mySortFunction = function(item) {
  if(isNaN(item[$scope.sortExpression]))
   return item[$scope.sortExpression];
  return parseInt(item[$scope.sortExpression]);
 }
}

To make the application work as it used to earlier, we need to apply a small change to the HTML. Name of the module has to be assigned to the ng-app directive. Modify the ng-app declaration in the div element to:
<div ng-app="shopping">
....
</div>

Let’s create a simple filter to display price in rupee format and add it to the above module. Following is the code of the filter:
angular.module(‘shopping’,[])
 .value(…)
 .filter(‘rupee’, function(){
  return function(item){
   return "Rs. "+item+".00";
  }
 });

Using this filter is similar to using currency filter.
<td>{{item.Price | rupee}}</td>

This sample is available on jsfiddle:

Happy coding!

3 comments:

  1. Hi Ravi,

    Nice blog item. Really pioneering stuff in Angular.

    The only thing I missed was, clicking on a table row could have brought it up in edit mode in the text box widgets below and the values could be updated :)

    But great.

    Thanks

    Vinay

    ReplyDelete
    Replies
    1. Hi Vinay,

      I am really glad that you found this post helpful. I created a separate fiddle to include the edit item functionality that you asked. You can find the fiddle here: http://jsfiddle.net/sravikiran/9EznM/2/

      Delete
  2. "As controller requires the functions defined in the service shopping_cart_service" but 'shopping_cart_operations' used in the code

    ReplyDelete

Note: only a member of this blog may post a comment.