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)?

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/


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'"

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

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

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/


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


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'

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 .


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

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

czy to normalne, że klucze (k, i) w objectList są przekształcane na 0 i 1?
Ken Vernaillen
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.