Sorting by Multiple Fields with Different Sort Orders Using the AngularJS orderby Filter

I’m working on a new, interactive, interface to the $100,000 Club dataset I keep on itsalljustcomics.com. The data deals with the world’s most valuable comic books (those worth more than $100,000) and it’s always just been a static list. Since I’ve started keeping track of that data, the number of entries has steadily grown to include hundreds of books. Because of that growth and in concert with the relaunch of that site, I’ve started to put together an AngularJS based interface to the data. One of the things I need to do is sort the displayed data by three different fields and with two different sorts. This is very easy to do in Angular, you just might not know it since the documentation for orderBy is crummy. The one example that’s there is more confusing than it needs to be and only shows one variation.

Here’s how you do it.

ng-repeat

For starters, this example uses the ng-repeat directive. ng-repeat makes it trivial to loop through a collection and apply an HTML template to every member of the collection. In this example items is a collection of JavaScript objects stored in an array on the controller’s scope.

app.controller('MainCtrl', function($scope) {
  $scope.items = [{
    "title":"Action Comics",
    "issue":"1",
    "publisher":"DC Comics",
    "cover_date":"1938-06-01",
    "pedigree":"Church",
    "grade_src":"Anecdote",
    "grade":"9.2",
    "general_commentary":"Best Existing",
    "uid":0}]
});


Every time through the loop the value of that particular item in the array is exposed to the template as it and any properties of that object are available using familiar dot notation. Those values are included in the template using a familiar bracket pattern and Angular recognizes them, interpolating the values into the page.


<tr data-ng-repeat="it in items">
  <td>{{it.title}}</td>
  <td>{{it.issue}}</td>
  <td>{{it.pedigree}}{{it.collection}}{{it.provenance}}</td>
  <td>{{it.grade_src}}</td>
  <td>{{it.grade}}</td>
  <td>{{it.general_commentary}}</td>
</tr>

You’ll notice I don’t just throw ng-repeat in there. While ng-repeat will work as an attribute, I prepend data- to the attribute name. While this doesn’t matter to Angular, it’s the way the HTML specification defines adding custom attributes, so I use that pattern for consistency across all my code.

This initial version would output something like the following:

out-of-order

orderBy

While the previous example works fine, the rows are simply presented in the order of the members of the array. That might be okay for you if you’re getting a presorted collection, but if you want to change the order, you have the power to adjust it using Angular’s orderBy filter.

In this example we want to sort by the title of the comic (ascending, from A to Z), then by the issue number (ascending, from 1-∞) and finally by the grade (descending, from 10.0 to 0.5.) While this is dead easy to do using Angular, it’s not clearly documented.

This is where the superheroics happen. Inside the ng-repeat you invoke the orderBy filter on your data. This is done with the pipe “|” character. This will be a familiar pattern for you if you’ve hacked around on the Unix command line. Everything on the left of the pipe is passed to the filter referenced on the right of the pipe. In this case we’re using orderBy but it could be any of the built-in filters or a custom filter of your own devising. Commonly, orderBy is shown accepting a single variable (technically an Angular expression) representing one of the available properties of the members of the collection. What’s not clear from the documentation or common examples is that is that it can also accept an array of properties with which to order the collection.1 So passing ['title','issue','-grade'] to orderBy tells Angular to sort first by the title, then by the issue and finally by the grade properties of the it object. Notice something interesting about the way grade is passed into orderBy? Yes, it’s that simple to reverse the sort in this syntax- simply negate the variable with “-” and the sort on that property will be reversed. Pretty sweet.


<tr data-ng-repeat="it in items | orderBy:['title','issue','-grade']"> 
  <td>{{it.title}}</td>
  <td>{{it.issue}}</td>
  <td>{{it.pedigree}}{{it.collection}}{{it.provenance}}</td>
  <td>{{it.grade_src}}</td>
  <td>{{it.grade}}</td>
  <td>{{it.general_commentary}}</td>
</tr>

Here’s a plunker with a working demo. This example is basically Exhibit A of what I like about Angular and what drives me crazy about it at the same time. I love the elegant, declarative syntax that really does feel like HTML rewritten for web apps. I’m giddy over how powerful some of the pieces are- like the directives and filters we’ve seen here. The out of the box stuff is great and custom filters and directives are just such a powerful option.

And then there’s the documentation. While the documentation is mostly correct, and they provide some form of documentation for pretty much everything in the library, none of the documentation is great. It often reads like it’s written by an Angular engineer who’s been tasked with writing documentation- technically correct, but often bare-bones and written from an insider’s or experts perspective. For an example of the latter see the usage of the word “predicate” in the documentation of orderBy. Quick show of hands- how many of you know what that means? Obviously some of you will, but many of the self-taught hackers that populate the web developer sphere won’t and the documentation suffers because it often errs on the side of the experts.2

1. It can also accept a more complicated Angular expression or a function reference.
2. They promise it’s going to get better, so I’ll keep my fingers crossed, but I’ve been tempted to start an unofficial AngularJS documentation project on Github to do an end-around the 808 issues and counting in their issue queue (many of which are documentation PRs).

9 thoughts on “Sorting by Multiple Fields with Different Sort Orders Using the AngularJS orderby Filter

  1. Excellent article. And entirely valid attack of what is monstrously bad Google documentation for AngularJS.

    So much so, I am repeating what I did for Delphi in 2002 (www.delphibasics.co.uk) – write an accessible, richly exampled basics site for Angular that presumes no prior Angular knowledge. Without your article, how would anyone guess that an array rather than a comma-separated list of fields would be needed for multiple sorts? Not only is Angular badly documented, but the design is frequently perverse with an unintuitive syntax.

    Compare the AngularJS page on <input? fields :

    http://docs.angularjs.org/api/ng/directive/input

    with what I hope is a more organised and more complete offering :

    http://www.angularbasics.co.uk?id=70

Leave a Reply

Your email address will not be published. Required fields are marked *