AngularJS-Sortierung nach Eigenschaft

93

Ich versuche, einige Daten nach Eigenschaften zu sortieren. Hier ist ein Beispiel, von dem ich dachte, dass es funktionieren sollte, aber es funktioniert nicht.

HTML-Teil:

<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-Teil:

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},
    }

}]);

Und das Ergebnis:

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

... dass IMHO so aussehen sollte:

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

Habe ich etwas verpasst (hier ist JSFiddle zum Experimentieren bereit )?

PrimosK
quelle

Antworten:

148

Der orderBy-Filter von AngularJS unterstützt nur Arrays - keine Objekte. Sie müssen also einen eigenen kleinen Filter schreiben, der die Sortierung für Sie übernimmt.

Oder ändern Sie das Format der Daten, mit denen Sie umgehen (wenn Sie Einfluss darauf haben). Ein Array mit Objekten kann nach nativem orderBy-Filter sortiert werden.

Hier ist mein orderObjectBy- Filter für 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;
 }
});

Verwendung aus Ihrer Sicht:

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

Das Objekt benötigt in diesem Beispiel ein Positionsattribut, aber Sie haben die Flexibilität, jedes Attribut in Objekten (die eine Ganzzahl enthalten) nur per Definition in der Ansicht zu verwenden.

Beispiel JSON:

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

Hier ist eine Geige, die Ihnen die Verwendung zeigt: http://jsfiddle.net/4tkj8/1/

Armin
quelle
1
Das ist toll. Ich habe die Option zum Sortieren von asc / desc hinzugefügt: app.filter ('orderObjectBy', function () {Rückgabefunktion (Eingabe, Attribut, Richtung) {if (! Angular.isObject (Eingabe)) return input; var array = [] ; for (var objectKey in input) {array.push (input [objectKey]);} array.sort (Funktion (a, b) {a = parseInt (a [Attribut]); b = parseInt (b [Attribut]) ; return direction == 'asc'? a - b: b - a;}); return array;}}); In HTML: <tr ng-repeat = "val in list | orderObjectBy: 'prop': 'asc'">
Jazzy
@Armin was ist, wenn dies ein objektähnliches Array ist? dh{1:'Example 1', 2:'Example 2', 3:'Example 3', ...}
Eugene
8
Tolle Antwort, ABER wir haben auf diese Weise den Schlüssel des Objekts verloren. Um es zu behalten, fügen Sie es einfach hinzu und erstellen Sie die Zeilen eines neuen Arrays im Filter, z. B. so, for(var objectKey in input) { input[objectKey]['_key'] = objectKey; array.push(input[objectKey]); }wie wir es verwenden können<div ng-repeat="value in object | orderObjectBy:'order'" ng-init="key = value['_key']">
Nicolas Janel
7
Es ist erwähnenswert, dass Angular.js jetzt das Sortieren nach einer Eigenschaft in einem Array von Objekten unterstützt : ... | orderBy: 'name'.
Wildhoney
2
@Wildhoney Bei dieser Frage geht es darum, Objekte mit Schlüsseln zu bestellen, keine Arrays, die Objekte enthalten.
Armin
31

Es ist ziemlich einfach, mach es einfach so

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

ng-repeat="prop in props | orderBy:'order'"
Leon
quelle
33
Dies funktioniert nicht für assoziative Arrays, worum es bei der Frage geht.
MFB
7

Vergessen Sie nicht, dass parseInt () nur für Integer-Werte funktioniert. Um Zeichenfolgenwerte zu sortieren, müssen Sie Folgendes austauschen:

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

mit diesem:

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

Wie Sie im Code von angle-JS ( https://github.com/angular/angular.js/blob/master/src/ng/filter/orderBy.js ) sehen können, funktioniert ng-repeat nicht mit Objekten. Hier ist ein Hack mit 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
quelle
4

laut http://docs.angularjs.org/api/ng.filter:orderBy sortiert orderBy ein Array. In Ihrem Fall übergeben Sie ein Objekt, sodass Sie Ihre eigene Sortierfunktion implementieren müssen.

oder ein Array übergeben -

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

Schauen Sie sich http://jsfiddle.net/qaK56/ an.

Gal Ben-Haim
quelle
Ich weiß, dass es mit Arrays funktioniert. Die Lösung besteht also darin, meine eigene Sortierfunktion zu schreiben?
PrimosK
Deine jsfiddle! = Dein Code, den du gepostet hast. Dies ist die richtige jsfiddle für Ihr Beispiel hier: jsfiddle.net/qaK56/92
mrzmyr
3

Sie sollten Ihre JSON-Struktur wirklich verbessern, um Ihr Problem zu beheben:

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

Dann könnten Sie tun

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

Ich denke, das Problem ist, dass die Variablen (Schlüssel, Wert) dem orderBy-Filter nicht zur Verfügung stehen und Sie sowieso keine Daten in Ihren Schlüsseln speichern sollten

Joshua Wooward
quelle
2
sag das Firebase;)
MFB
Soweit ich weiß, erstellen Sie die Daten in Firebase
Joshua Wooward
Wenn Sie ein Array in Firebase speichern, wird eine UID als Schlüssel für das Array verwendet. Es gibt viele Situationen, in denen man keine Kontrolle über die Datenstruktur bekommt, so dass Ihr Vorschlag möglicherweise etwas umfassend ist.
MFB
Speichern Sie eine Reihe von was? Man hat immer die Kontrolle über die Datenstruktur. Auch das OP erwähnt nichts über Firebase.
Joshua Wooward
2

Hier ist was ich getan habe und es funktioniert.
Ich habe gerade ein String-Objekt verwendet.

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

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

Wenn ich nach Text sortieren wollte, würde ich tun

orderBy : 'mostRecent.text'
James Harrington
quelle
2

Ich werde meine aktualisierte Version des Filters hinzufügen, die die nächste Syntax unterstützt:

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

Vielen Dank an Armin und Jason für ihre Antworten in diesem Thread und an Alnitak in diesem Thread .

TachikomaGT
quelle
1

Armins Antwort + eine strenge Überprüfung auf Objekttypen und nicht eckige Schlüssel wie $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
quelle
1

Das Folgende ermöglicht die Reihenfolge von Objekten nach Schlüssel ODER nach einem Schlüssel innerhalb eines Objekts .

In der Vorlage können Sie Folgendes tun:

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

Oder auch:

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

Der Filter:

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
quelle
Ist es normal, dass die Schlüssel von (k, i) in objectList in Nullen und Einsen umgewandelt werden?
Ken Vernaillen