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 для экспериментов)?

PrimosK
источник

Ответы:

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;
        }
    })

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

TachikomaGT
источник
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;
 }
})
dipole_moment
источник
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;
 }
})
dipole_moment
источник
нормально ли, что ключи (k, i) в objectList преобразуются в 0 и 1?
Ken Vernaillen 09