AngularJS сортировка по свойству


93

Я пытаюсь отсортировать некоторые данные по свойствам. Вот пример, который, как я думал, должен работать, но это не так.

HTML-часть:

<div ng-app='myApp'>
    <div ng-controller="controller">
    <ul>
        <li ng-repeat="(key, value) in testData | orderBy:'value.order'">
            {{value.order}}. {{key}} -> {{value.name}}
        </li>
    </ul>
    </div>
</div>

Часть JS:

var myApp = angular.module('myApp', []);

myApp.controller('controller', ['$scope', function ($scope) {

    $scope.testData = {
        C: {name:"CData", order: 1},
        B: {name:"BData", order: 2},
        A: {name:"AData", order: 3},
    }

}]);

И результат:

  1. A -> AData
  2. B -> BData
  3. C -> CData

... это ИМХО должно выглядеть так:

  1. C -> CData
  2. B -> BData
  3. A -> AData

Я что-то пропустил (вот и готовый JSFiddle для экспериментов)?

Ответы:


148

Фильтр orderBy в AngularJS поддерживает только массивы - без объектов. Поэтому вам нужно написать собственный небольшой фильтр, который будет выполнять сортировку за вас.

Или измените формат данных, с которыми вы работаете (если у вас есть на это влияние). Массив, содержащий объекты, можно сортировать с помощью собственного фильтра orderBy.

Вот мой фильтр orderObjectBy для AngularJS:

app.filter('orderObjectBy', function(){
 return function(input, attribute) {
    if (!angular.isObject(input)) return input;

    var array = [];
    for(var objectKey in input) {
        array.push(input[objectKey]);
    }

    array.sort(function(a, b){
        a = parseInt(a[attribute]);
        b = parseInt(b[attribute]);
        return a - b;
    });
    return array;
 }
});

Использование в вашем представлении:

<div class="item" ng-repeat="item in items | orderObjectBy:'position'">
    //...
</div>

В этом примере объекту требуется атрибут позиции, но у вас есть возможность использовать любой атрибут в объектах (содержащих целое число) только по определению в поле зрения.

Пример JSON:

{
    "123": {"name": "Test B", "position": "2"},
    "456": {"name": "Test A", "position": "1"}
}

Вот скрипка, которая показывает вам использование: http://jsfiddle.net/4tkj8/1/


1
Это круто. Я добавил возможность сортировки asc / desc: app.filter ('orderObjectBy', function () {return function (input, attribute, direction) {if (! Angular.isObject (input)) return input; var array = [] ; for (var objectKey во входных данных) {array.push (input [objectKey]);} array.sort (function (a, b) {a = parseInt (a [attribute]); b = parseInt (b [attribute]) ; направление возврата == 'asc'? a - b: b - a;}); return array;}}); В HTML: <tr ng-repeat = "val in list | orderObjectBy: 'prop': 'asc'">
Jazzy

@Armin что, если это объект, подобный массиву? ie{1:'Example 1', 2:'Example 2', 3:'Example 3', ...}
Евгений

8
Отличный ответ, НО таким образом мы потеряли ключ объекта. Чтобы сохранить его, просто добавьте его, создав новые строки массива в фильтре, например, for(var objectKey in input) { input[objectKey]['_key'] = objectKey; array.push(input[objectKey]); }как это мы можем использовать<div ng-repeat="value in object | orderObjectBy:'order'" ng-init="key = value['_key']">
Николас Джанель

7
Стоит отметить , что Angular.js делает сортировку поддержки свойства в массиве объектов в настоящее время: ... | orderBy: 'name'.
Wildhoney

2
@Wildhoney Этот вопрос касается упорядочивания объектов с помощью ключей, а не массивов, содержащих объекты.
Armin

31

Это довольно просто, просто сделай это вот так

$scope.props = [{order:"1"},{order:"5"},{order:"2"}]

ng-repeat="prop in props | orderBy:'order'"

33
Это не работает для ассоциативных массивов, о чем и идет речь.
MFB

7

Не забывайте, что parseInt () работает только для целочисленных значений. Чтобы отсортировать строковые значения, вам нужно поменять местами это:

array.sort(function(a, b){
  a = parseInt(a[attribute]);
  b = parseInt(b[attribute]);
  return a - b;
});

с этим:

array.sort(function(a, b){
  var alc = a[attribute].toLowerCase(),
      blc = b[attribute].toLowerCase();
  return alc > blc ? 1 : alc < blc ? -1 : 0;
});

6

Как видно из кода angular-JS ( https://github.com/angular/angular.js/blob/master/src/ng/filter/orderBy.js ), ng-repeat не работает с объектами. Вот хак с sortFunction.

http://jsfiddle.net/sunnycpp/qaK56/33/

<div ng-app='myApp'>
    <div ng-controller="controller">
    <ul>
        <li ng-repeat="test in testData | orderBy:sortMe()">
            Order = {{test.value.order}} -> Key={{test.key}} Name=:{{test.value.name}}
        </li>
    </ul>
    </div>
</div>

myApp.controller('controller', ['$scope', function ($scope) {

    var testData = {
        a:{name:"CData", order: 2},
        b:{name:"AData", order: 3},
        c:{name:"BData", order: 1}
    };
    $scope.testData = _.map(testData, function(vValue, vKey) {
        return { key:vKey, value:vValue };
    }) ;
    $scope.sortMe = function() {
        return function(object) {
            return object.value.order;
        }
    }
}]);

4

согласно http://docs.angularjs.org/api/ng.filter:orderBy , orderBy сортирует массив. В вашем случае вы передаете объект, поэтому вам придется реализовать свою собственную функцию сортировки.

или передать массив -

$scope.testData = {
    C: {name:"CData", order: 1},
    B: {name:"BData", order: 2},
    A: {name:"AData", order: 3},
}

взгляните на http://jsfiddle.net/qaK56/


Я знаю, что он работает с массивами .. Выходит, написать свою функцию сортировки?
PrimosK

Ваш jsfiddle! = Ваш код, который вы опубликовали. Это правильный jsfiddle для вашего примера здесь: jsfiddle.net/qaK56/92
mrzmyr

3

Вам действительно следует улучшить структуру JSON, чтобы решить вашу проблему:

$scope.testData = [
   {name:"CData", order: 1},
   {name:"BData", order: 2},
   {name:"AData", order: 3},
]

Тогда ты мог бы сделать

<li ng-repeat="test in testData | orderBy:order">...</li>

Я думаю, проблема в том, что переменные (ключ, значение) недоступны для фильтра orderBy, и вы все равно не должны хранить данные в своих ключах.


2
скажи это Firebase;)
MFB

насколько я знаю, вы создаете данные в базе данных
Джошуа Вовард,

Если вы храните массив в Firebase, он использует UID в качестве ключа для массива. Есть много ситуаций, когда никто не может управлять структурой данных, поэтому ваше предложение может быть несколько широким.
MFB

хранить массив чего? Всегда есть контроль над структурой данных. Также OP ничего не упоминает о Firebase.
Джошуа Вовард,

2

Вот что я сделал, и это работает.
Я просто использовал строковый объект.

$scope.thread = [ 
  {
    mostRecent:{text:'hello world',timeStamp:12345678 } 
    allMessages:[]
  }
  {MoreThreads...}
  {etc....}
]

<div ng-repeat="message in thread | orderBy : '-mostRecent.timeStamp'" >

если бы я хотел отсортировать по тексту, я бы сделал

orderBy : 'mostRecent.text'

2

Я добавлю свою обновленную версию фильтра, которая поддерживает следующий синтаксис:

ng-repeat="(id, item) in $ctrl.modelData | orderObjectBy:'itemProperty.someOrder':'asc'

app.filter('orderObjectBy', function(){

         function byString(o, s) {
            s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
            s = s.replace(/^\./, '');           // strip a leading dot
            var a = s.split('.');
            for (var i = 0, n = a.length; i < n; ++i) {
                var k = a[i];
                if (k in o) {
                    o = o[k];
                } else {
                    return;
                }
            }
            return o;
        }

        return function(input, attribute, direction) {
            if (!angular.isObject(input)) return input;

            var array = [];
            for(var objectKey in input) {
                if (input.hasOwnProperty(objectKey)) {
                    array.push(input[objectKey]);
                }
            }

            array.sort(function(a, b){
                a = parseInt(byString(a, attribute));
                b = parseInt(byString(b, attribute));
                return direction == 'asc' ? a - b : b - a;
            });
            return array;
        }
    })

Спасибо Армину и Джейсону за их ответы в этой ветке и Альнитаку в этой ветке .


1

Ответ Армина + строгая проверка типов объектов и неугловых ключей, таких как $resolve

app.filter('orderObjectBy', function(){
 return function(input, attribute) {
    if (!angular.isObject(input)) return input;

    var array = [];
    for(var objectKey in input) {
      if (typeof(input[objectKey])  === "object" && objectKey.charAt(0) !== "$")
        array.push(input[objectKey]);
    }

    array.sort(function(a, b){
        a = parseInt(a[attribute]);
        b = parseInt(b[attribute]);
        return a - b;
    });

    return array;
 }
})

1

Следующее позволяет упорядочивать объекты по ключу ИЛИ по ключу внутри объекта .

В шаблоне вы можете сделать что-то вроде:

    <li ng-repeat="(k,i) in objectList | orderObjectsBy: 'someKey'">

Или даже:

    <li ng-repeat="(k,i) in objectList | orderObjectsBy: 'someObj.someKey'">

Фильтр:

app.filter('orderObjectsBy', function(){
 return function(input, attribute) {
    if (!angular.isObject(input)) return input;

    // Filter out angular objects.
    var array = [];
    for(var objectKey in input) {
      if (typeof(input[objectKey])  === "object" && objectKey.charAt(0) !== "$")
        array.push(input[objectKey]);
    }

    var attributeChain = attribute.split(".");

    array.sort(function(a, b){

      for (var i=0; i < attributeChain.length; i++) {
        a = (typeof(a) === "object") && a.hasOwnProperty( attributeChain[i]) ? a[attributeChain[i]] : 0;
        b = (typeof(b) === "object") && b.hasOwnProperty( attributeChain[i]) ? b[attributeChain[i]] : 0;
      }

      return parseInt(a) - parseInt(b);
    });

    return array;
 }
})

нормально ли, что ключи (k, i) в objectList преобразуются в 0 и 1?
Ken Vernaillen 09
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.