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!

17 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
  4. Really awesome.. It helped me a lot.

    Can you please let me know if it is possible to sort ascending or descending on each column when we click on coulumn header ? Can you please give one example ?

    Thanks in advance

    ReplyDelete
  5. This comment has been removed by the author.

    ReplyDelete
  6. This comment has been removed by the author.

    ReplyDelete
  7. Hi, thanks for sharing, how can i check if the filtered item doesn't exist?

    ReplyDelete
  8. This comment has been removed by the author.

    ReplyDelete
  9. if i will use this
    {{items[$index].Name}}
    {{items[$index].Price | currency}}
    {{items[$index].Quantity}}

    instead of
    {{item.Name}}
    {item.Price | currency}}
    {{item.Quantity}}

    Then how to sort? pls post the technique! i am waiting..

    ReplyDelete
  10. Filter is not working. How to do it? I am not able to find the cause.

    ReplyDelete
  11. how to filter price in descending order?

    ReplyDelete
  12. Thank you for posting this! I have been searching for over an hour on this topic, and your post is the easiest to understand! Great example :)

    ReplyDelete
  13. I need to bold the matched filtered text in table. How can I do that??

    ReplyDelete
    Replies
    1. Use ng-class in your element e.g:

      ng-class="{'class-to-add':filtered-expression}"

      =)

      Delete

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