Tri AngularJS par propriété

93

Ce que j'essaie de faire, c'est de trier certaines données par propriété. Voici un exemple qui, selon moi, devrait fonctionner mais ce n'est pas le cas.

Partie 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>

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

}]);

Et le résultat:

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

... que IMHO devrait ressembler à ceci:

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

Ai-je manqué quelque chose (voici JSFiddle prêt à expérimenter)?

PrimosK
la source

Réponses:

148

Le filtre orderBy d'AngularJS ne prend en charge que les tableaux - pas d'objets. Vous devez donc écrire votre propre petit filtre, qui fait le tri pour vous.

Ou changez le format des données que vous gérez (si vous avez une influence sur cela). Un tableau contenant des objets peut être trié par filtre orderBy natif.

Voici mon filtre orderObjectBy pour 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;
 }
});

Utilisation à votre avis:

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

L'objet a besoin dans cet exemple d'un attribut de position, mais vous avez la flexibilité d'utiliser n'importe quel attribut dans les objets (contenant un entier), juste par définition dans la vue.

Exemple JSON:

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

Voici un violon qui vous montre l'utilisation: http://jsfiddle.net/4tkj8/1/

Armin
la source
1
C'est bien. J'ai ajouté l'option pour trier 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 [attribut]); b = parseInt (b [attribute]) ; sens de retour == 'asc'? a - b: b - a;}); tableau de retour;}}); En HTML: <tr ng-repeat = "val in list | orderObjectBy: 'prop': 'asc'">
Jazzy
@Armin et si c'est un objet comme un tableau? ie{1:'Example 1', 2:'Example 2', 3:'Example 3', ...}
Eugene
8
Excellente réponse MAIS nous avons perdu la clé de l'objet de cette façon. Pour le garder, il suffit de l'ajouter en créant de nouvelles lignes de tableau dans le filtre par exemple for(var objectKey in input) { input[objectKey]['_key'] = objectKey; array.push(input[objectKey]); }Comme ça on peut utiliser<div ng-repeat="value in object | orderObjectBy:'order'" ng-init="key = value['_key']">
Nicolas Janel
7
Il convient de noter que angularjs fait en charge le tri par une propriété dans un tableau d'objets maintenant: ... | orderBy: 'name'.
Wildhoney
2
@Wildhoney Cette question concerne l'ordre des objets avec des clés, pas des tableaux contenant des objets.
Armin
31

C'est assez facile, fais-le comme ça

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

ng-repeat="prop in props | orderBy:'order'"
Léon
la source
33
Cela ne fonctionne pas pour les tableaux associatifs, c'est sur quoi porte la question.
MFB
7

N'oubliez pas que parseInt () ne fonctionne que pour les valeurs Integer. Pour trier les valeurs de chaîne, vous devez permuter ceci:

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

avec ça:

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

Comme vous pouvez le voir dans le code de angular-JS ( https://github.com/angular/angular.js/blob/master/src/ng/filter/orderBy.js ) ng-repeat ne fonctionne pas avec les objets. Voici un hack avec 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
la source
4

selon http://docs.angularjs.org/api/ng.filter:orderBy , orderBy trie un tableau. Dans votre cas, vous passez un objet, vous devrez donc implémenter votre propre fonction de tri.

ou passer un tableau -

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

jetez un œil à http://jsfiddle.net/qaK56/

Gal Ben-Haim
la source
Je sais que cela fonctionne avec des tableaux. La solution est donc d'écrire ma propre fonction de tri?
PrimosK
Votre jsfiddle! = Votre code que vous avez posté. C'est le bon jsfiddle pour votre exemple ici: jsfiddle.net/qaK56/92
mrzmyr
3

Vous devriez vraiment améliorer votre structure JSON pour résoudre votre problème:

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

Alors tu pourrais faire

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

Le problème, je pense, est que les variables (clé, valeur) ne sont pas disponibles pour le filtre orderBy, et vous ne devriez pas stocker de données dans vos clés de toute façon

Joshua Wooward
la source
2
dites-le à Firebase;)
MFB
pour autant que je sache, vous créez les données dans Firebase
Joshua Wooward
Si vous stockez un tableau dans Firebase, il utilise un UID comme clé pour le tableau. Il existe de nombreuses situations où l'on ne contrôle pas la structure des données, donc votre suggestion pourrait être un peu radicale.
MFB
stocker un tableau de quoi? On a toujours le contrôle sur la structure des données. De plus, l'OP ne mentionne rien sur Firebase.
Joshua Wooward
2

Voici ce que j'ai fait et cela fonctionne.
Je viens d'utiliser un objet stringifié.

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

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

si je voulais trier par texte, je le ferais

orderBy : 'mostRecent.text'
James Harrington
la source
2

J'ajouterai ma version améliorée du filtre capable de prendre en charge la syntaxe suivante:

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

Merci à Armin et Jason pour leurs réponses dans ce fil, et Alnitak dans ce fil .

TachikomaGT
la source
1

La réponse d'Armin + une vérification stricte des types d'objets et des clés non angulaires telles que $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 dipolaire
la source
1

Ce qui suit permet de classer les objets par clé OU par clé dans un objet .

Dans le modèle, vous pouvez faire quelque chose comme:

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

Ou même:

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

Le filtre:

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 dipolaire
la source
est-il normal que les clés de (k, i) dans objectList soient transformées en 0 et 1?
Ken Vernaillen