AngularJS orderBy for multi-column sorting in different sort directions…


This is a fun issue to approach. I had a table of items which needed specific sorting on two fields (both descending) but the main sort needed to be dynamic based on one of five other fields with order (asc vs desc) toggling.

I’m using angularjs in this project. I had no trouble sorting by a single field and toggling the order understanding paystubs. I could also use orderBy to sort by multiple fields, but they all require the same ordering, learn about Lake Zurich Web Designers. There may be a trick to this that I couldn’t figure out, but the problem I was seeing is this:

I was using a $scope variable to designate the order direction called “reverse”. The column headings in my table had an ng-click event added to change another $scope variable called “orderByField” and would reverse the boolean of “reverse”.

 

This made the sorting dynamic for the column clicked, and if the column was clicked again, the order direction would switch as well. I’m not including the controller code because it’s just setting default values for the $scope.reverse and $scope.orderByField values.

This was fine until a change request was made to add additional sorting rules which aren’t easily accomplished with angular’s orderBy filter. The stipulation is that one field should always be ordered a certain way inside each set of orderByField groups.

Let me explain it with an example to better illustrate the issue. This is a simplified version of what I was dealing with:

items = [
  { start_time: '10', guaranteed: 1, name: 'test 1' },
  { start_time: '10', guaranteed: 0, name: 'test 2' },
  { start_time: '11', guaranteed: 1, name: 'test 3' },
  { start_time: '11', guaranteed: 0, name: 'test 4' }
];

The idea was if the column heading representing the start_time field is clicked that the items in the array would be sorted by start_time (either desc or asc). So start_time = 10 would be grouped together and start_time = 11 would be grouped together. Then within each of those groups, the ‘guaranteed’ field always needs to be sorted descending. So all guaranteed = 1 should be at the top of the start_time groups. There was actually a third field ordering stipulation within those groups but for simplification we’ll leave that one out.

My first thought was to use something like this:

 

It’s not recommended to use orderBy this way for good reason. It doesn’t work correctly. It worked fine in Firefox actually, but failed miserably in Chrome. The recommend method of sorting by multiple columns is to use something like the following:

 

The reason this wouldn’t work for me is because the “reverse” order direction switches not just the order direction of orderByField but also ‘guaranteed’. This wasn’t the desired result.

My next step was to create a custom orderBy filter which would do what I wanted. This example is more verbose in the if statements than it needs to be, but it’s easy to read this way:

app.filter("myOrderBy", function() {
    return function(items, field, reverse) {
        // sort by selected field first
        if(!reverse) {
            items.sort(function(a, b) {
                if(a[field]  >  b[field]) { return  1; }
                if(a[field] == b[field]) {
                    if(a['guaranteed']  <  b['guaranteed']) { return 1;  }
                    if(a['guaranteed'] == b['guaranteed']) { return 0;  }
                    if(a['guaranteed']  >  b['guaranteed']) { return -1; }
                }
                if(a[field]  <  b[field]) { return -1; }
            });
        } else {
            items.sort(function(a, b) {
                if(a[field]  <  b[field]) { return  1; }
                if(a[field] == b[field]) {
                    if(a['guaranteed']  <  b['guaranteed']) { return 1;  }
                    if(a['guaranteed'] == b['guaranteed']) { return 0;  }
                    if(a['guaranteed']  >  b['guaranteed']) { return -1; }
                }
                if(a[field]  >  b[field]) { return -1; }
            });
        }
        return items;
    }
});
  1. No comments yet.
(will not be published)