Friday, 8 February 2013

Filtering and Sorting data using Angular JS

In last post, we saw how Angular JS helps us in two-way data binding. As I mentioned earlier, Angular JS offers much more than just data binding. We will explore the filtering and sorting features offered by Angular JS.

Angular has a set of filters defined to display data to the user in certain format. We can create our own filters if we are not happy with any of the existing filter. Check Angular’s official developer guide to know more about what filters offer. In this post we will see the usage of some of the built-in filters.

I continue with the same data as I used in the last post. Here is the Angular JS controller with data that we will use in this post. We will add some members to the controller later in the post:

function ShoppingCartCtrl($scope)  {
  $scope.items = [
   {Name: "Soap", Price: "25", Quantity: "10"},
   {Name: "Shaving cream", Price: "50", Quantity: "15"},
   {Name: "Shampoo", Price: "100", Quantity: "5"}
  ];
}

Filtering
Data in an array can be filtered using Angular JS in two ways.

In one approach, we can filter data irrespective of the attributes. The filter is applied across the data in an array. Following markup shows it:

Search: <input type="text" ng-model="searchText" />

<table border="1">
 <thead>
  <tr>
   <th>Name</th>
   <th>Price</th>
   <th>Quantity</th>
  </tr>
 </thead>
 <tbody>
  <tr ng-repeat="item in items | filter:searchText">
   <td>{{item.Name}}</td>
   <td>{{item.Price}}</td>
   <td>{{item.Quantity}}</td>
  </tr>
 </tbody>
</table>

 Initially, the table will display all items present in the items array. As we type some text in the text box, data in the array will be filtered based on the entered text and the filtered data will immediately appear on the list.

As you observe, we didn’t ask Angular to filter data based on a property. This is a general filter. It is applied across the properties.

To filter data based on a property, we have to make some changes to the filter specified above. Following markup segment shows how to do it:

Search by any: <input type="text" ng-model="search.$" />

Search by name: <input type="text" ng-model="search.Name" />

<table border="1">
    <thead>
     <tr>
      <th>Name</th>
      <th>Price</th>
      <th>Quantity</th>
     </tr>
    </thead>
    <tbody>
     <tr ng-repeat="item in items | filter:search">
      <td>{{item.Name}}</td>
      <td>{{item.Price}}</td>
      <td>{{item.Quantity}}</td>
     </tr>
    </tbody>
   </table>

Text entered in the first text box will be used to filter data irrespective of property; it behaves just as our previous case. But the text entered in the second text box will be used to filter data by name alone.

Since price is a dollar amount, we can display the value with a dollar symbol using currency filter.

<td>{{item.Price | currency}}</td>


Sorting
Data in an array can be sorted based on values of a property. We can do it using the orderBy filter.

Let’s add a drop down list to the above sample and sort data based on the selected property. Following markup demonstrated this:

<div>Sort by: 
        <select ng-model=&quot;sortExpression&quot;>
  <option value="Name">Name</option>
  <option value="Price">Price</option>
  <option value="Quantity">Quantity</option>
 </select>
</div>
<table border="1">
 <thead>
  <tr>
   <th>Name</th>
   <th>Price</th>
   <th>Quantity</th>
  </tr>
 </thead>
 <tbody>
  <tr ng-repeat="item in items | orderBy:sortExpression | filter:search">
   <td>{{item.Name}}</td>
   <td>{{item.Price | currency}}</td>
   <td>{{item.Quantity}}</td>
  </tr>
 </tbody>
</table>

As soon as a property is selected from drop-down, you will see the data in the table displayed is sorted according to that property. Observe the sorting pattern of numeric columns (Price and Quantity). They are sorted like strings, not like numbers. This is the default behaviour of the orderBy filter, as mentioned in the documentation.

This behaviour can be overridden by defining a function. If we return value in the form of integer from this function, orderBy filter will apply numeric sorting. Following function can be used to sort string and integer type values. It has to be added to controller:

$scope.mySortFunction = function(item) {
 if(isNaN(item[$scope.sortExpression]))
  return item[$scope.sortExpression];
 return parseInt(item[$scope.sortExpression]);
}

Replace sortExpression of orderBy filter with mySortFunction to sort Price and Quantity as numbers.
<tr ng-repeat="item in items | orderBy:mySortFunction | filter:search">

The mySortFunction checks if the value passed is a number. If it is not a number, it returns the value as is. Otherwise, it parses the value as integer and returns the value.

You can play with the sample on jsfiddle:
Happy coding!

6 comments:

  1. Thanks a lot for your mySortFunction. This is the solution I was looking for. Great job! It will fix a bug in my next release (https://github.com/boxplayer/eTuneBook).

    ReplyDelete
    Replies
    1. Martin,
      You are welcome. I am glad that you could use this learning in your project.

      Delete
  2. post how to use $filter in controller

    ReplyDelete
    Replies
    1. Sure Vignesh, give me some time

      Delete
    2. I just posted the article: http://sravi-kiran.blogspot.in/2013/12/InvokingAngularJsFiltersFromController.html

      Delete
  3. Can you show me how i can do that filter inside my controller ?

    ReplyDelete