Multiple Column Sorting of a Table Using Angular

Thursday, June 20, 2013

This year I have been focusing a lot on Single Page Application (SPA) and the various tools that can be used to build these applications efficiently.
 
So far, I have checked out knockout.js, backbone.js and angular.js, but have been mainly focused on getting my head around angular.js. I have been hearing great things about ember.js too, so perhaps I'll tackle that next. For now, though, I am focusing on angular.js. 
 
So in this post, I thought I would share how to take advantage of angular's filtering functionality. Specifically: how to have a table with a sort button on each of the table columns. It's actually pretty easy.

Binding

One of the core features of angular.js is its ability to do "two-way binging". That is, once the page loads, Angular, binds any models on the its scope to the designated fields on the page. These fields can be a single record or a collection of records. This means properties I change on the scope will immediately change on the screen. Angular handles all that magic.

Filter

Angular has a filter function that allows you to do things like query your collection filtering the result on page. The filter function can also sort your collection instantaneously on the page. In my example of sorting, I can also change the sort the sort column dynamically and I can also reverse the sort dynamically by storing the sort values in the $scope. Note that the $scope is sort of like a ViewBag if you think of it in terms of ASP.Net ASP. It is holds properties that the page has access to and that you can change at anytime.

Template

Here is what my template looks like
<table class="table table-striped table-condensed">
        <thead>
            <tr>
                <th></th>
                <th>Client Name <button class="btn btn-mini btn-link" ng-click="sort('clientName')"><i  class="icon-sort"></i></button> </th>
                <th>Contact Name<button class="btn btn-mini btn-link" ng-click="sort('contactName')"><i  class="icon-sort"></i></button></th>
                <th>Email Address<button class="btn btn-mini btn-link" ng-click="sort('contactEmail')"><i  class="icon-sort"></i></button></th>
                <th></th>
                <th></th>
            </tr>
        </thead>
        <tbody>
            <tr ng-repeat="client in clients | orderBy:orderProp:direction">
                <td><a ng-href="/#/clients/edit/{{client.clientId}}"><i class="icon-edit"></i></a></td>
                <td><a ng-href="/#/clients/view/{{client.clientId}}"> {{client.clientName}}</a></td>
                <td>{{client.contactName}}</td>
                <td>{{client.contactEmail}}</td>
                <td></td>
                <td><remove-button text="Remove Client" action="removeClient()"></remove-button></td>
            </tr>
        </tbody>
    </table>

I tell Angular to loop through the collection by using the ng-repeat command and then I can set the sorting options by using the filter command orderBy. I am then telling it what property in the scope to use for the sorting (orderProp) and which property will be used for ascending and descending direction (direction).

Also notice my heading column links. They have a ng-click command to call the function sort. The sort function takes a parameter for which column to sort and will either change the column sorting or the direction depending on what column was clicked.

Then in the controller class, I initialize those properties and create the sort function which checks to see if it was the same column that is set in the scope and if it is it simply changes the direction of the sort. If the column is different than what is in scope, then it resort based on the new column and resets the direction to ascending.

                    $scope.direction = true;
		    $scope.orderProp = "clientName";
		    $scope.sort = function(column) {
		        if ($scope.orderProp === column) {
		            $scope.direction = !$scope.direction;
		        } else {
		            $scope.orderProp = column;
		            $scope.direction = true;
		        }
		    };

Here is my Plunker Example.

 

comments powered by Disqus