Tuesday 24 November 2015







HTTP is a stateless protocol. Each time a client retrieves a new information, it opens a seperate connection to the web / application server. The server does not automatically maintains the contextual information of the client. It leads to many difficulties. Typical example being the online store. User adds the products on the cart by visiting various views. 



There are various solutions available to maintain the session.
e.g. using the cookies, URL re-writting and using the hidden fields.



In online store application created using angular we can also maintain the state of the cart across the various views and controllers.

How we can do it in angular?

Angular provides a way to create services using various recipes which can hold the data.


Step 1. Create a service using value recipe. 
Step 2. Create a cart object inside this service.
Step 3. Provide the cart manipulation operations like update, delete, add Cart to this service.
step 4: Inject this service to the controllers in the online store application.
step 5. Angular makes the service available to all the controllers wherever this cart Service is injected. 

When the angular application bootstraps, it creates service which holds the empty cart object. 

All objects in angular being singleton the same object is made available to all the controllers thereby maintaining the state of the cart across the views.



Online store example

In typical shopping cart we buy products on various product / category pages and keep updating the cart. Here are the steps.

Step 1:  Create the angular application file say main.js

var app = angular.module('shopingCart', ['ngRoute']);

app.config(['$routeProvider', function($routeProvider) {
  $routeProvider
    .when('/page1', {
      tempateUrl: 'page1.html',
      controller: 'page1cntrl'
    })
    .when('/page2', {
      templateUrl: 'page2.html',
      controller: 'page2cntrl',
    })
    .when('/showCart', {
      templateUrl: 'cart.html',
      controller: 'cartcntrl',
    })   

    .otherwise({ // default action of route
      redirectedTo: '/page1'
    })
}]);
Step 2: Create the index.html file


    Shopping Cart
 
  
 
Step 3 :  Create a js file called session-service.js

Here we create the custom injectable service having a cart inside using the "value provider recipe". 

'use strict';
function Cart() {
  return {
    'cartId': '',
    'cartItem': []
  };
}
// custom service maintains the cart along with its behavior to clear itself , create new , delete Item or update cart

app.value('sessionService', {
  cart: new Cart(),
  clear: function() {
    this.cart = new Cart();
    // mechanism to create the cart id 
    this.cart.cartId = 1;
  },
  save: function(session) {
    this.cart = session.cart;
  },
  updateCart: function(productId, productQty) {
    this.cart.cartItem.push({
      'productId': productId,
      'productQty': productQty
    });
  },
//delete Item and other cart operations function goes here...
});

Product page 1 and its controller
page1.html

Product 1 Page

Added to Cart

page1cntrl.js

'use strict';
angular.module('shopingCart').controller('page1cntrl',['$scope','sessionService',function($scope,sessionService){
// in this controller the sessionService has been injected as dependency
// retrieve the session information from the service to the scope using the following stt.
    $scope.sessionData  = sessionService;
    $scope.updateCart = function(id,qty) {
    $scope.itemBought = true;
    sessionService.updateCart(id,qty); // call the service method to update the cart.
  };
}]);

page2.html

Product 2 Page

Added to Cart

page2cntrl.js
'use strict';
angular.module('shopingCart').controller('page1cntrl',['$scope','sessionService',function($scope,sessionService){
// in this controller the sessionService has been injected as dependency
// retrieve the session information from the service to the scope using the following stt.
    $scope.sessionData  = sessionService;
 // for demo and understanding purpose we have duplicated the same method in the two controllers.
// how ever we can create the commonMethods which can be accessed from various different controllers.
I will soon be posting how to create the common methods which can be accessed by different controllers.

  $scope.updateCart = function(id,qty) {
   $scope.itemBought = true;
   sessionService.updateCart(id,qty); // call the service method to update the cart.
  };

}]);
cart.html

Cart

Product idproduct Qty
{{cartItem.id}} {{cartItem.qty}}
Cart is Empty
cartcntrl.js
'use strict';
  angular.module('shopingCart').controller("cartcntrl",
            ['$scope','sessionService',
                       function($scope,sessionService) {
                                  $scope.sessionData = sessionService;
                                   $scope.varShowCart = true;
                                   sessionService.save($scope.sessionData);
          }]);


Note:

This session object needs to be stored in local storage like "$window.sessionStorage" , session object or any other mechanism. The current object will be lost once the user refreshes the page.
 

Further reading

There are different recipes (methods) using which we can create custom angular service these are
1. Service
2. Factory
3. Value
4. Constant and
5. $provide

The first  4 are the specialized version of the $provide. For more details you can refer https://docs.angularjs.org/api/auto/service/$provide



Friday 20 November 2015



Question : How can we share information between the two different angular controllers? 

Answer: Inter controller communication in Angularjs can be achieved using the following mechanisms.

1. Using the $rootScope
2. By Using one of the mechanism for creating the custom services in Angularjs.
    These are...
     a) Factory Method
     b) Value Method
     c) Service Method or
     d) Provide Method.

In this article we will use the factory method for inter controller communication in Angularjs

Problem Statement:

We have a List of available Stores in the UI. These list is made available from one controller "storeList".

There is another controller "storeInfo" which displays the store information.

Once the store is selected in the "storeList" controller the corresponding store information should be made available in the "storeInfo" controller which actually glues this information to the view.

Solution :

Code:
Selected Store is: {{selectedStore}}


Controllers
var storeApp = angular.module('storeApp',[])
storeApp.factory('currentCustomer',function($rootScope) {
  var currentCustomer =  {};
  currentCustomer.customerID = 0;
  currentCustomer.customerName = '';
      currentCustomer.getCustomers =  function () {
          return [{CustomerID:1,CustomerName:'Store 1'},
                  {CustomerID:2,CustomerName:'Store 2'},
                  {CustomerID:3,CustomerName:'Store 3'}]; 
      };
      currentCustomer.setCurrentCustomer = function (custObject) {
        this.customerID = custObject['CustomerID'];
        this.ustomerName = custObject['CustomerName'];
        this.publishChanges();
      }; 
      
      currentCustomer.getCustomerID = function(){
        return this.customerID;
      };
  
      currentCustomer.getCustomerName = function(){
        return this.customerName; 
      };      
      currentCustomer.publishChanges = function() {
              $rootScope.$broadcast('custDataChangeEvent');
      };
    return currentCustomer; 
});  
    

// controller storeList

    storeApp.controller('storeList',function($scope,$http,currentCustomer,$filter) {
      $scope.customers = currentCustomer.getCustomers();
      $scope.changeCust = function changeCust(id) {
          var filteredCustomerList = $filter('filter')($scope.customers,{CustomerID:id},true);
          var currCustomer = filteredCustomerList[0];
          currentCustomer.setCurrentCustomer(currCustomer);
      };
    });

      // listen to the events
      storeApp.controller('storeInfo',function($scope,currentCustomer) {
      $scope.$on('custDataChangeEvent',function(){
       $scope.selectedStore = currentCustomer.getCustomerID();
      });
});

Explanation:

All the Objects in AngularJs are singleton.

In this example currentCustomer is the factory service created.  The factory method returns the currentCustomer object consisting of the customerID and customerName attributes along with the necessary methods like
1. get customer List
2. get CustomerId and getCustomerName
3. set customer details.
4. and importantly a method "publishChanges" which actually notifies the updates made in the currentCustomer  object.

To publish the changes we used the $broadcast service provided by angular. Please check my post on the $broadcast and $emit and $on services.

I will be soon posting it.

storeList controller :

It retrieves the customer list from the factory method and keeps it in its controller scope.
Once the user selects the store from the select option the id is passed to the changeCust scope method. From the customer array which available in the controller, object is filtered using the id.  To filter a particular record from the array we have used the $filter service provided by angular.  Please have a look at my post on the filter.   I will be soon posting it.

The selected customer is then set as the current customer using the factory methods setCurrentCustomer

storeInfo controller:

This controller listens to the publish event. As soon as the customer details are updated. currentCustomer factory's publish method broadcasts the changes to all the controllers. The controllers which are listening to this event a callback function is called where we can take the appropriate action against this changes.

in our example we update the customerID information to the scope selectedStore which is glued back to the view.

There are other ways to achieve this. One way is to use the provider recipes like value or factory.
Please check another article http://yogeshtutorials.blogspot.in/2015/11/session-tracking-in-angularjs.html

Thanks.