Orderby not working with dict syntax on ng-repeat

2019-01-14 20:10发布

I am trying to use ng-repeat with a dictionary style syntax and apply an order to the key value.

(key, value) in something | orderBy:'key'

It seems OrderBy isn't working as expected

Example here http://jsfiddle.net/mhXuW/

6条回答
男人必须洒脱
2楼-- · 2019-01-14 20:36

I created a custom filter to make this possible for a select list:

.filter('toDictionaryArray', function () {
    return function (obj) {
        if (!(obj instanceof Object)) return obj;

        var arr = [];
        for (var key in obj) {
            arr.push({ key: key, value: obj[key] });
        }
        return arr;
    }
})

the select list ng-options then reads as follows:

<select ng-options="kv.key as kv.value for kv in p.options | toDictionaryArray | orderBy:'value'"></select>
查看更多
够拽才男人
3楼-- · 2019-01-14 20:40

Here's a good work around (angular-toArrayFilter):

.filter('toArray', function () {
  return function (obj, addKey) {
    if ( addKey === false ) {
      return Object.keys(obj).map(function(key) {
        return obj[key];
      });
    } else {
      return Object.keys(obj).map(function (key) {
        if(typeof obj[key] == 'object') return Object.defineProperty(obj[key], '$key', {     enumerable: false, value: key});
      });
    }
  };
});
查看更多
贪生不怕死
4楼-- · 2019-01-14 20:41

Instead of using orderby filter which only supports arrays and not objects. You can write your own filter like this I took the code from Justin Klemm http://justinklemm.com/angularjs-filter-ordering-objects-ngrepeat/ and added support for specifying multiple ordering fields. The filter example below is to filter based on properties in the values but you can easily change it to filter based on the key as well.

app.filter('orderObjectBy', function()
{
    return function(items, fields, reverse)
    {
        var filtered = [];
        angular.forEach(items, function(item)
        {
            filtered.push(item);
        });

        filtered.sort(function (a, b)
        {
            var result = 0;

            for (var i = 0; i < fields.length; ++i)
            {
                if (a[fields[i]] > b[fields[i]])
                {
                    result = 1;
                    break;
                }
                else if (a[fields[i]] < b[fields[i]])
                {
                    result = -1;
                    break;
                }
            }

            return result;
        });

        if (reverse)
        {
            filtered.reverse();
        }

        return filtered;
    };
});

Then inside your html5 web page use it like so where statistics is a map/dictionary with key value pairs.

<div class="row" ng-repeat="stat in statistics | orderObjectBy: 'NumTimesPartnered', 'NumTimesOpponent']:true">
    <div class="col col-33">{{stat.Name}}</div>
    <div class="col"><center>{{stat.NumTimesPartnered}}</center></div>
    <div class="col"><center>{{stat.NumTimesOpponent}}</center></div>
</div>
查看更多
迷人小祖宗
5楼-- · 2019-01-14 20:42

The parameters to orderBy must match property names in an array of objects.

Your data needs to look something like this:

$scope.list2 = [ { id:"2013-01-08T00:00:00", name:'Joe'},
                 { id:"2013-01-09T00:00:00", name:'Sue'}];

Then a filter like this will work:

<div ng-repeat="item in list2 | orderBy:'id':true">

Fiddle.

Note that orderBy works on the entire array (something in your sample code above) and it returns a sorted array. orderBy doesn't know anything about key and value.

查看更多
混吃等死
6楼-- · 2019-01-14 20:42

this is not implemented. please see here:

https://github.com/angular/angular.js/issues/1286

EDIT (Aug 27, 2013): this issue seems to be resolved now.

查看更多
对你真心纯属浪费
7楼-- · 2019-01-14 20:51

As akonsu says, the feature has been requested here. However, I still couldn't get it to work with the latest (1.3.0) beta.

There's a nice workaround buried in the comments (note this requires underscore, and you'll have to replace references to 'key' with 'value.$key' in the above example):

[Add a filter]:

app.filter('toArray', function() { return function(obj) {
    if (!(obj instanceof Object)) return obj;
    return _.map(obj, function(val, key) {
        return Object.defineProperty(val, '$key', {__proto__: null, value: key});
    });
}});

Example markup:

<div ng-repeat="val in object | toArray | orderBy:'priority' | filter:fieldSearch">
  {{val.$key}} : {{val}} 
</div>

Potential downsides:

  • Object.defineProperty will not work like this in IE8 (if you don't mind making $key enumerable then this is an easy change).
  • If your property values are not objects then you can't set the $key property and this fails.
  • It is not directly integrated into orderBy or ngRepeat so the syntax is different.
查看更多
登录 后发表回答