web stats

AngularJS table sort

I’ve had a chance to play around with AngularJS recently. The first thing I came up against was having a table where you could sort each column by clicking the header. At first I did all sorts of crazy things like writing my own directive until I stumbled upon the orderBy filter.

The first thing I found out is that you need to keep it simple by calling a property on an object – using getter functions won’t quite work. But knowing that we can take a look at the orderBy example in the AngularJS documentation. It looks handy but there is a problem around how reverse works:

<div ng-controller="Ctrl">
<pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
<hr/>
[ <a href="" ng-click="predicate=''">unsorted</a> ]
<table class="friend">
<tr>
<th><a href="" ng-click="predicate = 'name'; reverse=false">Name</a>
(<a href="" ng-click="predicate = '-name'; reverse=false">^</a>)</th>
<th><a href="" ng-click="predicate = 'phone'; reverse=!reverse">Phone Number</a></th>
<th><a href="" ng-click="predicate = 'age'; reverse=!reverse">Age</a></th>
</tr>
<tr ng-repeat="friend in friends | orderBy:predicate:reverse">
<td>{{friend.name}}</td>
<td>{{friend.phone}}</td>
<td>{{friend.age}}</td>
</tr>
</table>
</div>

In the example we can view “name” normally by clicking it or in reverse by clicking on the arrow. I wanted it so that clicking again would reverse the order. It looks like phone and age do this, but can you spot the problem? the issue is they both change reverse, so if I click on phone then things will be reversed and clicking on age will give me age without reverse. What I needed was that clicking a column the first time would reset reverse, so how do we go about that?

<div ng-controller="Ctrl">
<pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
<hr/>
[ <a href="" ng-click="predicate=''">unsorted</a> ]
<table class="friend">
<tr>
<th><a href="" ng-click="reverse = predicate == 'name' && !reverse; predicate = 'name'">Name</a></th>
<th><a href="" ng-click="reverse = predicate == 'phone' && !reverse; predicate = 'phone'">Phone Number</a></th>
<th><a href="" ng-click="reverse = predicate == 'age' && !reverse; predicate = 'age'">Age</a></th>
</tr>
<tr ng-repeat="friend in friends | orderBy:predicate:reverse">
<td>{{friend.name}}</td>
<td>{{friend.phone}}</td>
<td>{{friend.age}}</td>
</tr>
</table>
</div>

The magic happens with ng-click and we use && to work as a control statement. Because it’s an && it will only continue if the first expression is true, in this case we check to see if we’re already sorting by that column. If we’re not sorting by the column then we return false – so clicking on a new column will put reverse as false. The second part will then flip reverse for us if we are already sorted by that column.

Last thing to note is that we have set reverse but still need to set what we’re sorting by so we put that last (so it doesn’t get in the way of checking if we’re already set at the column).

And there we have it, the expected behaviour without the need of a new directive or controller.

Powered by WPeMatico