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

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/


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

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

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

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/


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


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'

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 .


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

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

est-il normal que les clés de (k, i) dans objectList soient transformées en 0 et 1?
Ken Vernaillen
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.