sorting values of a hash object in ngRepeat - angularJS

10,891

Solution 1

If you check the documentation it says that the expression in orderBy can be a function, a string or an Array. An therefore you need dateReleased to be a string: 'dateReleased'

Also you need your phones Object be an actual Array.

Try:

$scope.phones = [{
        "name":"Phone Name1",
        "dateReleased":"2012-1-09 15:48:24"
    },{
        "name": "Phone Name2",
        "dateReleased":"2012-3-12 15:32:11"
    },{
        "name": "Phone Name3",
        "dateReleased":"2012-2-10 13:53:32"
    }];

<li ng-repeat="phone in phones | orderBy:'dateReleased':true">
    <p>{{phone.name}}</p>
    <p>{{phone.dateReleased}}</p>
</li>

Solution 2

While ngRepeat can iterate a hash object, like $scope.phones in your example, the built-in orderBy filter will not work. I believe this is due to the way objects are stored. As other's have noted, you need to convert the hash to an array. While you can do this using the methods suggested above, I prefer to do it using a custom filter. This gives me the benefit of not having to alter my hash directly, and also let's me reuse the filter with other hashes.

yourApp.filter('orderObjectBy', function() {
  return function(items, field, reverse) {
    var filtered = [];
    angular.forEach(items, function(item) {
      filtered.push(item);
    });
    filtered.sort(function (a, b) {
      return (a[field] > b[field] ? 1 : -1);
    });
    if(reverse) filtered.reverse();
    return filtered;
  };
});

This filter converts the object into a standard array and sorts it by the field you specify. You can use the orderObjectBy filter exactly like orderBy, including a boolean value after the field name to specify whether the order should be reversed. In other words, false is ascending, true is descending.

<li ng-repeat="phone in phones | orderObjectBy:'dateReleased':true">
  <p>{{phone.name}}</p>
  <p>{{phone.dateReleased}}</p>
</li>

I've got a post on my blog regarding this topic.

Solution 3

Both of the other answers get you part way there, but not all the way...

You'll need to create a function on your scope that converts the object to an array like so:

$scope.phonesArray = function() {
    var result = [];
    angular.forEach($scope.phones, function(phone, id) {
      result.push(phone);
    });
    return result;
};

Then you'd call that instead of your object in your ngRepeat:

<li ng-repeat="phone in phonesArray() | orderBy:'dateReleased':true">
    <p>{{phone.name}}</p>
    <p>{{phone.dateReleased}}</p>
</li>

Also: Notice that 'dateReleased' is a string, so it knows to $eval that string off of the current item, otherwise it will check the parent $scope.dateReleased, which doesn't exist.

Here is a plunk for what I think you're trying to do

EDIT: You can also "convert" the object to an array and store it on the $scope, if you're worried about the function to do so being too "expensive", but that shouldn't be an issue, as you're developing a clientside app for one user, and not a server application for many users, meaning you have a little wiggle room for "expensive". (Which it won't be anyway)

Solution 4

First of all, you need to understand that ng:filter and ng:orderBy work with Arrays (an ordered collection of items), but you're trying to use them on Object (_un_ordered collection of items). One possible approach is to collect all the objects into an array, then proceed with ng-repeat on it instead. Like this:

<ul ng-init="phones = [
      {name:'Phone Name 1', dateReleased:'2011-1-09 15:48:24'}
    , {name:'Phone Name 2', dateReleased:'2012-3-12 15:32:11'}
    , {name:'Phone Name 3', dateReleased:'2012-2-10 13:53:32'}]; 
    pred = '-dateReleased';" >
    <li ng-repeat="phone in phones | orderBy:pred">
        <p>{{phone.name}}</p>
        <p>{{phone.dateReleased}}</p>
    </li>
</ul>
Share:
10,891
Chai Nadig
Author by

Chai Nadig

Updated on July 28, 2022

Comments

  • Chai Nadig
    Chai Nadig over 1 year

    I have an object like this:

    $scope.phones = new Object();
    $scope.phones['id1'] = {
        "name":"Phone Name1",
        "dateReleased":"2012-1-09 15:48:24"
    };
    $scope.phones['id2'] = {
        "name": "Phone Name2",
        "dateReleased":"2012-3-12 15:32:11"
    };
    $scope.phones['id3'] = {
        "name": "Phone Name3",
        "dateReleased":"2012-2-10 13:53:32"
    };
    

    I'm displaying this using ngRepeat. I'm not able to order by dateReleased. Also, ordering in reverse isn't working. My ngRepeat looks this:

    <li ng-repeat="phone in phones | orderBy:dateReleased:true">
        <p>{{phone.name}}</p>
        <p>{{phone.dateReleased}}</p>
    </li>
    
  • hakazvaka
    hakazvaka almost 10 years
    The code from your blog post doesn't work, while this one does... strange :) Thanks dude, saved my life!