Wednesday, 27 March 2013

Templates, Routing and Sharing Data Between Views in an Angular JS Application

Till last post, we saw the capabilities of Angular JS within a single page with a form and how to interact with service using AJAX. Another key feature that Angular JS provides is, view composition. We can compose multiple views on the same page, based on some templates which will be loaded as and when they are required. We will discuss about these features in this post.

To make an HTML page able to compose multiple views using AngularJS, we need to add an element with ng-view attribute. Content will be dynamically added inside this element based on certain rules defined by the programmer. Following is the mark-up inside the body tag after removing all content and adding a div with ng-view attribute:

<body ng-app="shopping">
    <div ng-view>
    </div>
</body>

The entire mark-up that we had earlier on the page is moved to AddRemoveItems.htm. No change has been made to the mark-up.
<div>
    Sort by:
    <select ng-model="sortExpression">
        <option value="Name">Name</option>
        <option value="Price">Price</option>
        <option value="Quantity">Quantity</option>
    </select>
</div>
<div>
    <strong>Filter Results</strong></div>
<table>
    <tr>
        <td>
            By Any:
        </td>
        <td>
            <input type="text" ng-model="search.$" />
        </td>
    </tr>
    <tr>
        <td>
            By Name:
        </td>
        <td>
            <input type="text" ng-model="search.Name" />
        </td>
    </tr>
    <tr>
        <td>
            By Price:
        </td>
        <td>
            <input type="text" ng-model="search.Price" />
        </td>
    </tr>
    <tr>
        <td>
            By Quantity:
        </td>
        <td>
            <input type="text" ng-model="search.Quantity" />
        </td>
    </tr>
</table>
<table border="1">
    <thead>
        <tr>
            <td>
                Name
            </td>
            <td>
                Price
            </td>
            <td>
                Quantity
            </td>
            <td>
                Remove
            </td>
        </tr>
    </thead>
    <tbody ng-repeat="item in items | orderBy:mySortFunction | filter: search">
        <tr>
            <td>
                {{item.Name}}
            </td>
            <td>
                {{item.Price | rupee}}
            </td>
            <td>
                {{item.Quantity}}
            </td>
            <td>
                <input type="button" value="Remove" ng-click="removeItem(item.ID)" />
            </td>
        </tr>
    </tbody>
</table>
<div>
    Total Price: {{totalPrice() | rupee}}</div>
<a ng-click="purchase()">Purchase Items</a>
<form name="itemForm" novalidate>
<table>
    <tr>
        <td>
            Name:
        </td>
        <td>
            <input name="name" type="text" ng-model="item.Name" required ng-pattern="name" />
        </td>
    </tr>
    <tr>
        <td>
            Price:
        </td>
        <td>
            <input name="price" type="text" ng-model="item.Price" required valid-price />
        </td>
    </tr>
    <tr>
        <td>
            Quantity:
        </td>
        <td>
            <input name="quantity" type="number" ng-model="item.Quantity" min="1" max="90" ng-pattern="integerval"
                required />
        </td>
    </tr>
    <tr>
        <td colspan="2">
            <input type="Button" value="Add" ng-click="addItem(item)" ng-disabled="itemForm.$invalid" />
        </td>
    </tr>
</table>
</form>
<div class="errorText">
    {{error}}</div>
<div ng-show="itemForm.name.$dirty && itemForm.name.$invalid">
    <span ng-show="itemForm.name.$error.required">Name cannot be left blank</span> <span
        ng-show="itemForm.name.$error.pattern">Name cannot contain numbers or special characters</span>
</div>
<div ng-show="itemForm.price.$dirty && itemForm.price.$invalid">
    <span ng-show="itemForm.price.$error.required">Price cannot be blank</span> <span
        ng-show="itemForm.price.$error.validPrice">Price should be a number between 50 and
        5000 with maximum 2 digits after decimal point</span>
</div>
<div ng-show="itemForm.quantity.$dirty && itemForm.quantity.$invalid">
    <span ng-show="itemForm.quantity.$error.required">Quantity cannot be blank</span>
    <span ng-show="itemForm.quantity.$error.pattern || itemForm.quantity.$error.min || itemForm.quantity.$error.max">
        Quantity should be an integer between 1 and 90</span>
</div>

Once a user adds all desired items to the cart, he should be able to check out the cart. For this, we should navigate the user to another page where the user can see the items on the cart and checkout the items. I created another template file, CheckoutItems.htm. This template will be rendered when the user clicks on checkout link on the AddremoveItems page. You can check the template from the project on Github.

The aim is to render one of these templates on the page based on the current URL. To make this happen, we need to configure routes to the module. Process of adding routes is similar to adding routes in Global.asax ASP.NET MVC. Each route will hold the following information:

  • URL template of the page
  • Path of the HTML template to be rendered
  • Name of the controller
Following is the syntax of configuring routes to the module:
module.config([‘$routeProvider’,<other dependencies>, function($routeProvider,<other parameters>){
 $routeProvider.when(‘<url-template>’,{templateUrl:’<path-of-html-template>’,controller:<name-of-controller>})
   .otherwise({redirectTo:’<default-url-template-to-render>’});
}]);

To learn more on routing, refer to Angular JS official documentation.

Let’s configure routes to shopping module keeping the above syntax in mind. Following are the templates added to the module in our application:

shoppingModule.config(['$routeProvider', function($routeProvider){
        $routeProvider.when('/addRemoveItems',{templateUrl:'Templates/AddRemoveItems.htm',controller:ShoppingCartCtrl})
                      .when('/checkoutItems',{templateUrl:'Templates/CheckoutItems.htm',controller:CartCheckoutCtrl})
                      .otherwise({redirectTo: '/addRemoveItems'});
    }]);

In the AddRemoveItems view, we should have a hyper link to navigate to the CheckoutItems page. When the user clicks on this link, we should send shopping cart data to the checkout view. Since both views have separate controllers, we have to store the data at a central place to make it accessible in the controller of second page. Let’s add a service to the shopping module to hold this data.
shoppingModule.service('shared',function(){
        var cartItems= [];
        return{
            getCartItems: function(){
                return cartItems;
            },
            setCartItems: function(value){
                cartItems=value;
            }
        };
    })

On click of the link on the AddRemoveItems page, the data has to be assigned to cartItems field in the above service and the control has to be navigated to checkout view. This is done using the following function:
$scope.purchase = function () {
        shared.setCartItems($scope.items);
        $window.location.href = "#/checkoutItems";
};

$window used in the above function is a service in Angular JS that references to the browser’s window object. Since we are using it in the controller, it should be injected through parameter. This data can be accessed in checkout view using the shared service. Following code shows how to do it:
function CartCheckoutCtrl($scope, shared){
    function loadItems() {
        $scope.items = shared.getCartItems();
    }
 
    loadItems();
}

Now we have a tiny shopping cart application with two pages. This is definitely not a great application, but it covers most of the useful features of Angular JS.

Complete code of the application is available in the following github repo: AngularShopping

Cart Run the application setting ShoppingCart-MultipleViews.htm as the start-up page. Notice the URL once the page is loaded on browser. Click on the link, it should move to the Checkout view, with an update to the URL. You can also switch between the views using browser’s back and forward buttons.

The point to be observed during navigation is, the page is not fully refreshed. You won’t see the progress bar showing load status when the navigation happens. This is because, we are rendering a small part of the page(portion in the div marked with ng-view) using a template.

Happy coding!

Wednesday, 13 March 2013

Moving AJAX calls to a custom service in Angular JS

In my last post on Angular JS, we moved the data into an ASP.NET Web API controller and invoked the data using $http, a service in Angular JS that abstracts AJAX calls. But the code resided in the controller and that is not good. Responsibility of the controller should be to sit between data and view, but not to call services using AJAX. So, let’s move the AJAX calls to a separate component.

The AJAX calls made using $http service are executed asynchronously. They return promise objects, using which their status can be tracked. As we will be moving them into a custom service, the controller depending on them should be notified with the status of completion. The notification can be sent using the promise object returned by the service functions or, we can create our own deferred object using $q service.

I discussed about deferred and promise in jQuery earlier in a post. Basic usage of the pattern remains the same with $q as well, except a small difference syntax. Following snippet is shows the way of creating a deferred object, calling success and failure functions using $q:

function myAsyncFunc(){
//Creating a deferred object
var deferred = $q.defer();
operation().success(function(){
    //Calling resolve of deferred object to execute the success callback
    deferred.resolve(data);
  }).error(function(){
    //Calling reject of deferred object to execute failure callback
    deferred.reject();
  });
  //Returning the corresponding promise object
  return deferred.promise;
}

While calling the above function, a callback to be called on success and a callback to be handled on failure should be attached. It might look as follows:
myAsyncFunction().then(function(data){
    //Update UI using data or use the data to call another service
  },
  function(){
    //Display an error message
  });

The functions responsible for making AJAX calls should follow this pattern as the functions in $http are executed asynchronously. To notify the dependent logic about state of execution of the functionality, we have to use the promise pattern.

As we will be enclosing this functionality in a custom service which requires $http for AJAX and $q for promise, these dependencies will be injected at the runtime when the module is loaded. Following snippet demonstrates the implementation of a function to retrieve items in shopping cart calling Web API service:

angular.module('shopping', []).
  factory('shoppingData',function($http, $q){
    return{
      apiPath:'/api/shoppingCart/',
      getAllItems: function(){
        //Creating a deferred object
        var deferred = $q.defer();

        //Calling Web API to fetch shopping cart items
        $http.get(this.apiPath).success(function(data){
          //Passing data to deferred's resolve function on successful completion
          deferred.resolve(data);
      }).error(function(){

        //Sending a friendly error message in case of failure
        deferred.reject("An error occured while fetching items");
      });

      //Returning the promise object
      return deferred.promise;
    }
  }
}

Following is the controller consuming the above custom service:
function ShoppingCartCtrl($scope, shoppingData) {
  $scope.items = [];

  function refreshItems(){
    shoppingData.getAllItems().then(function(data){
      $scope.items = data;
    },
    function(errorMessage){
      $scope.error=errorMessage;
    });
  };

  refreshItems();
};

As you see parameters of the controller, the second parameter shoppingData will be injected by the dependency injector during runtime. The function refreshItems follows the same convention as the snippet we discussed earlier. It does the following:
  • On successful completion of getAllItems() function, it assigns data to a property of $scope object which will be used to bind data
  • If the execution fails, it assigns the error message to another property of $scope object using which we can display the error message on the screen

The controller is free from any logic other than updating data that is displayed on the UI.

Functions to add a new item and remove an existing item also follow similar pattern. You can find the complete code in the following github repo: AngularShoppingCart

Happy coding!

Friday, 8 March 2013

Understanding Promise Pattern in JavaScript Using jQuery’s Deferred Object

Asynchronous programming is used almost everywhere these days. JavaScript is no exception. In rich JavaScript applications, we have to perform several tasks like calling a service using AJAX, caching data, applying animations and some new cases might arise while writing the application.

Irrespective of whether an operation is synchronous or asynchronous, we need a response from the when the operation is succeeded or even when it fails. Synchronous operations are usually enclosed within try…catch blocks to check for errors. As asynchronous operations run in background and we can’t determine when they will complete, their failures cannot be caught by the catch block. Even in case of successful completion, the response won’t be available for the immediate next statement. These cases are handled using callbacks.

We see several patterns in which callbacks are registered. While using raw AJAX invocations with XMLHttpRequest, we assign the callback to onreadystatechange property of the object, as shown below:

var xhr = new XMLHttpRequest();
xhr.open(“GET”,url, true);
xhr.send();

xhr.onreadystatechange = function(){
    if(xhr.readyState == 4){
        if(xhr.status == 200){
     successCallback(xhr.responseText);
        }
        else{
            failureCallback(xhr.responseText);
        }
    }
};

We also see the cases where the asynchronous function accepts a callback function as a parameter.
invokingAsyncFunction(successCallback(data), failureCallback(error));

These callbacks look good unless we have a chain of three or more asynchronous calls. When the number goes up, it becomes difficult to handle them and the code becomes less readable.

We can overcome this difficulty using the Promise pattern. The Promise pattern believes in returning a deferred object that can be used to call a piece of logic when the underlying asynchronous operation is either resolved or rejected. In other words, we can say the pattern promises us that it would send a result when it gets. The result can be data if the operation is successful or details of an error in case of failure.

The deferred object will hold the current state of the asynchronous operation. At any given time, the deferred object will have one of the three states: unresolved, resolved and rejected.

The deferred object would be in unresolved state when it is waiting for the asynchronous operation to complete. Once the operation is completed successfully, it enters resolved state. Otherwise, it enters rejected state.

jQuery library added support of deferred in jQuery 1.5. The library itself uses this feature for implementation of AJAX features.

jQuery’s deferred object makes it easy to write asynchronous code in JavaScript. Following steps must be followed while writing an asynchronous function using deferred object:

  1. Get a deferred object by calling the $.Deferred() constructor
  2. In case of success, call resolve function passing result of the operation
  3. In case of failure, call reject function passing the error details
  4. Return deferred’s promise object, which provides ways to attach callbacks and determine current state of the deferred object

With this understanding, let’s put the above AJAX logic in a function and make it asynchronous using jQuery’s deferred object:
function ajaxCallToAUrl(url){
  var deferred = $.Deferred();    //Getting a deferred object
  var xhr = new XMLHttpRequest();

  xhr.open(“GET”,url,true);
  xhr.send();

  xhr.onreadystatechange = function(){
    if(xhr.readyState == 4){
      if(xhr.status==200)
        deferred.resolve(xhr.responseText);  //Calling resolve() in case of success
      
      else
        deferred.reject(xhr.responseText);  //Calling reject() in case of failure
    }
  }
  return deferred.promise();  //Returning the promise object
}

Following are the steps to be followed while calling the above function:
  1. Call the function and store the returned promise in an object
  2. Attach a callback to done() function of promise. It will be called when the deferred object enters resolved state
  3. Attach a callback to fail() function of promise. It will be called when the deferred object enters rejected state

Following snippet demonstrates it:
var promise = ajaxCalToAUrl(url);
promise.done(function(data){
    //Update the UI to bind data obtained
}.fail(function(error){
    //Display the error message
});

The success and failure functions can also be attached to the promise object using then function as well. The then() function accepts three callbacks, first one is for success, second one is for failure and the third one is for handling progress. Failure and progress callbacks are optional. Progress callback is executed when deferred receives a progress notification. Above implementation can be expressed as follows using then function:
promise.then(function(data){
    //Update the UI to bind data obtained
}, function(error){
    //Display the error message
}, function(){
    //Notify user that the operation is in progress
});

Using then(), we can chain multiple operations as well. Following snippet demonstrates chaining:
var secondPromise = firstPromise.then(function(data){
    return callAnotherAsyncFunction(data);     //Assuming the function callAnotherAsyncFunction returns a promise
});

secondPromise.done(function(data){
    //Chained request is successful. Update UI with the data obtained
});
We can also combine multiple requests using $.when(). The when() function accepts multiple deferred objects. It returns a promise object. State of the when function changes to resolved when all the operations are successfully completed. If any of the operation is failed, state of when changes to rejected.
$.when(deferred1, deferred2).done(function(){
    //Logic to be executed on successful execution
}).fail(function(){
    //Logic to be executed upon failure of any of the operations
});

This is a beginning to using promises. jQuery's deferred object offers capabilities to forcefully reject the operation, notify change of status and some other features that we may require in an application.

References:


Documentation on jQuery’s official site about deferred objects
Article about deferred and promise on MSDN ScriptJunkie


Happy coding! 

Tuesday, 5 March 2013

AJAX using Angular JS

Angular JS defines a service, $http, that is capable of communicating with remote servers over HTTP using XmlHttpRequest. $http includes a service that takes a single configuration object where we configure all required properties for the remote asynchronous call. Following is the syntax of using $http service with configurations:
$http({method:’<method>’, url: ‘<service-url>’})
 .success(function(data, status, headers, config){
  //Logic to be executed upon successful completion of the request
  //Will be called asynchronousy
 })
 .error(function(data, status, headers, config){
 //Logic to be executed when the request is unsuccessful
 //Will be called asynchronously
});

We also have shortcut methods to send get, post, put, delete, head and JSONP requests. These methods accept URL, config. Post and put methods accept input data also as a parameter. To learn more about these methods and the input parameters, refer to $http service documentation.

 Let’s continue with our shopping cart example which I have used in all earlier posts (Last post in this series is Form validation using Angular JS). I created an ASP.NET Web API controller to operate on the cart items. We have to replace the logic of operating with cart items with AJAX calls to the Web API. I chose Web API to create the services because it makes creation and consumption of HTTP services very easy. If you are new to Web API, checkout Web API content on official ASP.NET website.

Following are the ShoppingCartItem and ShoppingCart classes, these classes are used to define structure of each item and to operate on the current items on the cart respectively.

public class ShoppingCartItem
{
    public int ID { get; set; }
    public string Name { get; set; }
    public float Price { get; set; }
    public int Quantity { get; set; }
}


public class ShoppingCart
{
    static List<ShoppingCartItem> cartItems;

    static ShoppingCart()
    {
        cartItems = new List<ShoppingCartItem>()
        {
            new ShoppingCartItem(){ID=1,Name="Soap",Price=25,Quantity=5},
            new ShoppingCartItem(){ID=2,Name="Shaving cream",Price=30,Quantity=15},
            new ShoppingCartItem(){ID=3,Name="Shampoo",Price=100,Quantity=5}
        };
    }
 
    public void AddCartItem(ShoppingCartItem item)
    {
        int nextID = cartItems.Max(sci => sci.ID) + 1;
        item.ID = nextID;
        cartItems.Add(item);
    }

    public IEnumerable<ShoppingCartItem> GetAllItems()
    {
        return cartItems;
    }

    public void RemoveItem(int id)
    {
        cartItems.RemoveAll(sci => sci.ID == id);
    }
}

In the controller, we just need to invoke the methods defined in the ShoppingCart class. This makes the controller pretty straight forward. Following is the implementation:
public class ShoppingCartController : ApiController
{
    ShoppingCart cart;
 
    public ShoppingCartController()
    {
        cart = new ShoppingCart();
    }
 
    // GET api/<controller>
    public IEnumerable<ShoppingCartItem> Get()
    {
        return cart.GetAllItems();
    }
 
    // POST api/<controller>
    public void Post(ShoppingCartItem item)
    {
        cart.AddCartItem(item);
    }
 
    // DELETE api/<controller>/5
    public HttpResponseMessage Delete(int id)
    {
        cart.RemoveItem(id);
        return new HttpResponseMessage(HttpStatusCode.OK);
    }
}

As we see, the URI templates to call each of these methods are specified in a comment above each method. For example, to call the get method to obtain all cart items, we will use the following logic:
$http.get('/api/shoppingCart/').success(function (data) {
     $scope.items = data;
    });

I am using the shorthand get method of http service here. We can obtain the same result using $http(options) as well. For the time being, I am placing the logic of AJAX calls in Controller to make the demo simple. We will move it into a module in another post. Following is the modified controller:
function ShoppingCartCtrl($scope, $http) {
 $scope.items = [];

 $scope.item = {};
 
 $scope.sortExpression = "Name";

 function refreshItems(){
  $http.get('/api/shoppingCart/').success(function (data) {
  $scope.items = data;
       });
 };
 
 $scope.addItem = function (item) {
  $http.post('/api/shoppingCart/', item).success(function(){
  refreshItems();
  });
  $scope.item = {};
                $scope.itemForm.$setPristine();
 };
 
 $scope.removeItem = function (id) {
  $http.delete('/api/shoppingCart/'+id).success(function(){
  refreshItems();
  });
 };
 
 $scope.mySortFunction = function(item) {
  if(isNaN(item[$scope.sortExpression]))
   return item[$scope.sortExpression];
  return parseInt(item[$scope.sortExpression]);
 };
 
 $scope.totalPrice = function(){
  var total = 0;
  for(count=0;count<$scope.items.length;count++){
   total += $scope.items[count].Price*$scope.items[count].Quantity;
  }
  return total;
 };
 
 $scope.name=/^[a-zA-Z ]*$/;
  
 $scope.integerval=/^\d*$/;

 refreshItems();
}

 Complete code of the sample is available on this github repo: AngularShoppingCart
I will be adding more code to this repo in the future.

Happy coding!