Friday 1 February 2013

Easy two-way data binding in HTML pages with Angular JS

The world is gradually drifting from heavy server logic to heavy client logic for web applications. We see a number of client side libraries being released for different purposes almost every day. These libraries help front-end web developers to make their applications rich.

Most of the times, we spend time in creating data driven applications, such as shopping cart, accounts management, exploring items like books, audio or video. Data binding becomes primary responsibility for a developer working on such applications. To exploit data binding on client side, we have several libraries like Angular, Knockout, Backbone, and Ember to make our work easier. These libraries also help us to write clean JavaScript code using one of MV* patterns.

Lately, I started exploring Angular JS. It is a great library. Though this post explains about data binding, Angular JS is capable of doing a lot more. Following is a list of features supported by Angular JS:

  • Data Binding
  • Data Templates
  • Filtering and Sorting
  • Validation
  • Support for AJAX
  • History
  • Routing
  • Modular components
  • Dependency injection
  • Encourages separation and testability

Because of these features, Angular JS makes client side development very easier.

Structure of an application using Angular JS
An application using Angular JS must have following components:

  • Reference to Angular JS library
  • A JavaScript controller containing data or with logic to fetch data

We have to add the ng-app declaration to a portion of the page that uses features of Angular JS. It can be added to a div inside a page wherein we have to use some expressions of Angular JS. If it is needed for the entire page, we have to add the ng-app declaration to the body tag.
<div ng-app>
 <!-- Your markup goes here -->
</div>

A demo showing some data binding features of Angular JS
Let’s create a shopping cart. We will add the features to calculate total price of the cart, adding items to cart and removing items from cart. We have to add this logic to the controller. An Angular JS controller must follow the following conventions:

  • It should be a JavaScript function accepting an argument named $scope
  • All of the model variables and functions should be added to the $scope object
  • Controllers are usually suffixed with Ctrl, but it is not necessary

Following is the controller with the required functionalities added:
function ShoppingCartCtrl($scope)  {
    $scope.items = [
        {Name: "Soap", Price: "25", Quantity: "10"},
        {Name: "Shaving cream", Price: "50", Quantity: "15"},
        {Name: "Shampoo", Price: "100", Quantity: "5"}
    ];

    $scope.addItem = function(item) {
        $scope.items.push(item);
 $scope.item = {};
    }
    
    $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.removeItem = function(index){
        $scope.items.splice(index,1);
    }
}

To bind data using members of the controller, we have to specify the controller using ng-controller directive. Ensure that the element containing ng-controller directive is a child of an element declared as ng-app.
<div ng-app>
 <div ng-controller="ShoppingCartCtrl">
  <!-- Your markup goes here -->
</div>
</div>

Let’s display the items added to items array in a table. Let’s also add a button on each row to remove the item. We have to use the following blocks to achieve this:

  • ng-repeat directive to iterate through items
  • Binding controller’s properties using {{}}
  • ng-click directive to bind button click event
  • $index to use current index in an array

Following is the table displaying items:

<table border="1">
    <thead>
 <tr>
  <td>Name</td>
  <td>Price</td>
  <td>Quantity</td>
                <td>Remove Item</td>
 </tr>
    </thead>
    <tbody>
 <tr ng-repeat="item in items">
  <td>{{item.Name}}</td>
  <td>{{item.Price}}</td>
  <td>{{item.Quantity}}</td>
  <td><input type="button" value="Remove" ng-click="removeItem($index)" /></td>
 </tr>
    </tbody>
</table>

Let’s display the total price of cart below this table. For this, we need to call the totalPrice() function of controller.
<div>Total Price: {{totalPrice()}}</div>

Finally, let’s add a form to accept values from user and add the new item to items array. We have to use ng-model directive to automatically add an input field to a property of the controller. Following is the form:
<table>
 <tr>
  <td>Name: </td>
  <td><input type="text" ng-model="item.Name" /></td>
 </tr>
 <tr>
  <td>Price: </td>
  <td><input type="text" ng-model="item.Price" /></td>
 </tr>
 <tr>
  <td>Quantity: </td>
  <td><input type="text" ng-model="item.Quantity" /></td>
 </tr>
 <tr>
  <td colspan="2"><input type="Button" value="Add" ng-click="addItem(item)" /> </td>
     
 </tr>
</table>

When we enter something in the text boxes, a property named item is automatically added to $scope. This property is used in the addItem function.

You can play with the sample using the following jsfiddle:



Try removing some items from the table and observe that totalPrice is updated immediately. It also happens when an item is added to the array as well.

As we saw, two-way data binding is made very easy using directives and binding syntax of Angular JS. All we need is a controller with some members in it. We don’t need to invoke any special functions to send notification when something changes in the controller. Everything is available for free to us, all we need to do is follow the conventions.

We will explore other features of Angular JS in future posts.

Happy coding!

12 comments:

  1. Nice example! I'd recommend placing the ng-repeat directive on the tr-tag instead of the tbody-tag. Right now you're repeating the tbody-tag, which is probably not what you were trying to do.

    ReplyDelete
    Replies
    1. Hi Martjin,

      Thanks for pointing. I will correct the code soon.

      Delete
  2. Hi,
    Can we trigger the addItem function with item value as parameter

    ReplyDelete
    Replies
    1. That is how it is done in the sample code.

      Delete
  3. Nice simple sample. I always wanted to use angular in a traditional MVC app for data binding.

    ReplyDelete
  4. can i have working example of authenticated - role-based login page in fiddle or plnkr

    ReplyDelete
    Replies
    1. Check this github repo: https://github.com/artgon/angularjs-role-based-auth

      Delete
  5. Great illustration. Good set of examples.
    I wrote on - How databinding works in Angular JS?
    I will love to get your feedback on this.

    ReplyDelete
  6. good example, but I still would like to know how should I do if I would like to modify an item instead of adding an item?

    ReplyDelete
    Replies
    1. Check this jsfiddle: http://jsfiddle.net/sravikiran/9EznM/2/

      Delete
    2. hi thanks for helping but I still have a problem.
      this.items[counter].$$hashKey == item.$$hashKey
      in modifyItem does not work. I don't know why?

      Delete
  7. I have the same problem with "this.items[counter].$$hashKey == item.$$hashKey"

    ReplyDelete

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