1

Angular sorts "Åland Islands" after "Zimbabwe". I would like to fix this in a global way. Rather than creating a custom sort function and specifying it in every orderBy filter, I would like to somehow globally patch the default sort function. Is this possible?

By first normalizing both strings with the following function, the problem is fixed:

function norm(str) {
  str = str.toLowerCase();
  str = str.replace(/\\s/g, "");
  str = str.replace(/[àáâãäå]/g, "a");
  str = str.replace(/æ/g, "ae");
  str = str.replace(/ç/g, "c");
  str = str.replace(/[èéêë]/g, "e");
  str = str.replace(/[ìíîï]/g, "i");
  str = str.replace(/ñ/g, "n");
  str = str.replace(/[òóôõö]/g, "o");
  str = str.replace(/œ/g, "oe");
  str = str.replace(/[ùúûü]/g, "u");
  str = str.replace(/[ýÿ]/g, "y");
  str = str.replace(/\\W/g, "");
  return str;
}

I'm hoping there's a way to do this once in Angular and never worry about it again.

3
  • 4
    Is creating a custom filter, say "betterOrderBy", acceptable? Commented Jan 24, 2013 at 3:50
  • I believe angularjs uses javascript native compare operators. Commented Jan 24, 2013 at 3:52
  • @MarkRajcok, that's a great idea. Sounds perfect as long as, other than this comparison fix, betterOrderBy behaves the same as orderBy. Commented Jan 24, 2013 at 21:11

2 Answers 2

5

You can normalize it yourself:

var normalize = function(str) {
  return str.toLowerCase().
             replace(/[àáâãäå]/g, "a");
             // ...
};

app.controller('MainCtrl', function($scope) {
  $scope.items = [
    {name: 'Åland Islands'},
    // ...
  ];

  $scope.normalizedName = function(item) {
    return normalize(item.name);
  };
});

And then use in html:

<li ng-repeat="item in items | orderBy:normalizedName">{{item.name}}</li>

Here's the whole example on plunker.

Sign up to request clarification or add additional context in comments.

1 Comment

Vojta, this makes it so easy to affect the sort order that I don't really need to do it in a global way.
3

As per the comments on the question, create a custom filter. Inject the orderBy filter into this custom filter to not duplicate what orderBy already does:

angular.module('myApp', []).
filter('betterOrderBy', function(orderByFilter) {
    return function(input, arg1, arg2) {
        var copy_of_input = angular.copy(input)
        for (var i = 0; i < input.length; i++) {
            copy_of_input[i]['orig'] = input[i];
            str = copy_of_input[i][arg1].toLowerCase();
            str = str.replace(/[àáâãäå]/g, "a");
            // ...
            copy_of_input[i][arg1] = str.replace(/\\W/g, "");
        }
        var normalized_sorted = orderByFilter(copy_of_input, arg1, arg2);
        var normalized_sorted_orig = [];
        angular.forEach(normalized_sorted, function(obj, i) {
            normalized_sorted_orig.push(obj.orig)
        })
        return normalized_sorted_orig;
    }
});

Use it as follows:

Better sort: {{myArrayOfObjects | betterOrderBy:'name'}}

Fiddle.

7 Comments

Changing the content of the array in a "orderBy"? Feels like there could be unexpected side effects
@Liviu, good point. I changed the code to create a copy first and then use the copy.
Thanks for the answer, @MarkRajcok. It looks like this filter ends up lower casing the names not only for sorting purposes, but also in the output.
@Christopher, yeah, for some reason I lost sight of the fact that the output text should be the same as the original. I modified my filter. It should do what you want now. I updated the answer's sample code and the fiddle.
@MarkRajcok I love this solution! Do you know how to do the same for filter? I tried with no success...
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.