AngularJS sortowanie według właściwości

93

To, co próbuję zrobić, to posortować niektóre dane według właściwości. Oto przykład, który moim zdaniem powinien działać, ale tak nie jest.

Część 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>

Część 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},
    }

}]);

A wynik:

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

... że IMHO powinno wyglądać tak:

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

Czy coś przeoczyłem (tutaj jest gotowy JSFiddle do eksperymentowania)?

PrimosK
źródło

Odpowiedzi:

148

Filtr orderBy w AngularJS obsługuje tylko tablice - żadnych obiektów. Musisz więc napisać własny mały filtr, który będzie sortował za Ciebie.

Lub zmień format danych, z którymi masz do czynienia (jeśli masz na to wpływ). Tablicę zawierającą obiekty można sortować według natywnego filtru orderBy.

Oto mój filtr orderObjectBy dla 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;
 }
});

Użycie Twoim zdaniem:

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

Obiekt potrzebuje w tym przykładzie atrybutu pozycji, ale można elastycznie używać dowolnego atrybutu w obiektach (zawierających liczbę całkowitą), tylko z definicji w widoku.

Przykład JSON:

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

Oto skrzypce, które pokazują użycie: http://jsfiddle.net/4tkj8/1/

Armin
źródło
1
To jest świetne. Dodałem opcję sortowania 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 (function (a, b) {a = parseInt (a [atrybut]); b = parseInt (b [atrybut]) ; kierunek powrotu == 'asc'? a - b: b - a;}); return array;}}); W HTML: <tr ng-repeat = "val in list | orderObjectBy: 'prop': 'asc'">
Jazzy
@Armin co jeśli to jest obiekt podobny do tablicy? tj.{1:'Example 1', 2:'Example 2', 3:'Example 3', ...}
Eugene
8
Świetna odpowiedź, ALE w ten sposób zgubiliśmy klucz obiektu. Aby to zachować, po prostu dodaj, tworząc nowe wiersze tablicy w filtrze, np. W for(var objectKey in input) { input[objectKey]['_key'] = objectKey; array.push(input[objectKey]); }ten sposób możemy użyć<div ng-repeat="value in object | orderObjectBy:'order'" ng-init="key = value['_key']">
Nicolas Janel
7
Warto zauważyć, że angularjs robi wsparcie sortując nieruchomości w tablicę obiektów teraz: ... | orderBy: 'name'.
Wildhoney
2
@Wildhoney To pytanie dotyczy porządkowania obiektów za pomocą kluczy, a nie tablic zawierających obiekty.
Armin
31

To całkiem proste, po prostu zrób to w ten sposób

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

ng-repeat="prop in props | orderBy:'order'"
Leon
źródło
33
To nie działa w przypadku tablic asocjacyjnych, o co chodzi w tym pytaniu.
MFB
7

Nie zapominaj, że parseInt () działa tylko dla wartości całkowitych. Aby posortować wartości ciągów, musisz zamienić to:

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

z tym:

array.sort(function(a, b){
  var alc = a[attribute].toLowerCase(),
      blc = b[attribute].toLowerCase();
  return alc > blc ? 1 : alc < blc ? -1 : 0;
});
Eric Steinborn
źródło
6

Jak widać w kodzie angular-JS ( https://github.com/angular/angular.js/blob/master/src/ng/filter/orderBy.js ), ng-repeat nie działa z obiektami. Oto hack z 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;
        }
    }
}]);
SunnyShah
źródło
4

zgodnie z http://docs.angularjs.org/api/ng.filter:orderBy , orderBy sortuje tablicę. W twoim przypadku przekazujesz obiekt, więc będziesz musiał zaimplementować własną funkcję sortowania.

lub przekaż tablicę -

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

spójrz na http://jsfiddle.net/qaK56/

Gal Ben-Haim
źródło
Wiem, że to działa z tablicami. Więc rozwiązaniem jest napisanie własnej funkcji sortowania?
PrimosK
Your jsfiddle! = Twój kod został opublikowany. Oto właściwy jsfiddle dla twojego przykładu tutaj: jsfiddle.net/qaK56/92
mrzmyr
3

Naprawdę powinieneś ulepszyć strukturę JSON, aby naprawić problem:

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

Wtedy mógłbyś to zrobić

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

Myślę, że problem polega na tym, że zmienne (klucz, wartość) nie są dostępne dla filtru orderBy i i tak nie powinieneś przechowywać danych w swoich kluczach

Joshua Wooward
źródło
2
powiedz to Firebase;)
MFB
o ile wiem, tworzysz dane w firebase
Joshua Wooward
Jeśli przechowujesz tablicę w Firebase, używa ona identyfikatora UID jako klucza tablicy. Jest wiele sytuacji, w których nie można uzyskać kontroli nad strukturą danych, więc Twoja sugestia może być nieco rozległa.
MFB
przechowywać tablicę czego? Zawsze ma się kontrolę nad strukturą danych. Również OP nic nie wspomina o Firebase.
Joshua Wooward
2

Oto, co zrobiłem i działa.
Właśnie użyłem obiektu ze strunami.

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

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

gdybym chciał sortować według tekstu, zrobiłbym to

orderBy : 'mostRecent.text'
James Harrington
źródło
2

Dodam moją ulepszoną wersję filtru, która obsługuje następną składnię:

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

Dziękuję Arminowi i Jasonowi za ich odpowiedzi w tym wątku oraz Alnitakowi w tym wątku .

TachikomaGT
źródło
1

Odpowiedź Armina + ścisłe sprawdzenie typów obiektów i kluczy innych niż kątowe, takich jak $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;
 }
})
moment dipolowy
źródło
1

Poniższe umożliwia uporządkowanie obiektów według klucza LUB klucza w obiekcie .

W szablonie możesz zrobić coś takiego:

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

Lub nawet:

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

Filtr:

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;
 }
})
moment dipolowy
źródło
czy to normalne, że klucze (k, i) w objectList są przekształcane na 0 i 1?
Ken Vernaillen