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

Фільтр AngularJS 'orderBy просто підтримує масиви - жодних об'єктів. Отже, ви повинні написати власний невеликий фільтр, який виконує сортування за вас.

Або змінити формат даних, з якими ви обробляєтесь (якщо ви на це впливаєте). Масив, що містить об'єкти, може бути відсортований за власним фільтром orderBy.

Ось мій замовленняObjectBy фільтр для 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 in input) {array.push (input [objectKey]);} array.sort (функція (a, b) {a = parseInt (a [атрибут]); b = parseInt (b [атрибут]) ; напрям повернення == 'asc'? a - b: b - a;}); масив повернення;}}); In HTML: <tr ng-repeat = "val у списку | orderObjectBy: 'prop': 'asc'">
Jazzy

@Armin що робити, якщо це такий об'єкт, як масив? тобто{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']">
Nicolas Janel

7
Варто відзначити , що Angular.js робить сортування підтримки властивості в масиві об'єктів в даний час: ... | orderBy: 'name'.
Wildhoney

2
@Wildhoney Це питання стосується впорядкування об'єктів за допомогою ключів, а не масивів, що містять об'єкти.
Армін

31

Це досить просто, просто зробіть це так

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

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

33
Це не працює для асоціативних масивів, про що йдеться.
MFB

7

Не забувайте, що parseInt () працює лише для значень Integer. Для сортування рядкових значень вам потрібно поміняти місцями:

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! = Ваш код u розміщений. Це правильний 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
Джошуа Вувард

Якщо ви зберігаєте масив у Firebase, він використовує UID в якості ключа для масиву. Є багато ситуацій, коли ніхто не отримує контролю над структурою даних, тому ваша пропозиція може бути дещо широкою.
MFB

зберігати масив чого? Завжди контролюється структура даних. Також ОП нічого не згадує про 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.