Fonction de tri personnalisé dans ng-repeat

113

J'ai un ensemble de tuiles qui affichent un certain nombre en fonction de l'option sélectionnée par l'utilisateur. Je voudrais maintenant implémenter un tri selon le nombre affiché.

Le code ci-dessous montre comment je l'ai implémenté (en obtenant / définissant une valeur dans la portée des cartes parents). Maintenant, comme la fonction orderBy prend une chaîne, j'ai essayé de définir une variable dans la portée de la carte appelée curOptionValue et de trier par cela, mais cela ne semble pas fonctionner.

La question devient donc: comment créer une fonction de tri personnalisée?

<div ng-controller="aggViewport" >
<div class="btn-group" >
    <button ng-click="setOption(opt.name)" ng-repeat="opt in optList" class="btn active">{{opt.name}}</button>
</div>
<div id="container" iso-grid width="500px" height="500px">
    <div ng-repeat="card in cards" class="item {{card.class}}" ng-controller="aggCardController">
        <table width="100%">
            <tr>
                <td align="center">
                    <h4>{{card.name}}</h4>
                </td>
            </tr>
            <tr>
                <td align="center"><h2>{{getOption()}}</h2></td>
            </tr>
        </table>        
    </div>
</div>

et contrôleur:

module.controller('aggViewport',['$scope','$location',function($scope,$location) {
    $scope.cards = [
        {name: card1, values: {opt1: 9, opt2: 10}},
        {name: card1, values: {opt1: 9, opt2: 10}}
    ];

    $scope.option = "opt1";

    $scope.setOption = function(val){
        $scope.option = val;
    }

}]);

module.controller('aggCardController',['$scope',function($scope){
    $scope.getOption = function(){
        return $scope.card.values[$scope.option];
    }
}]);
user1167650
la source

Réponses:

192

En fait, le orderByfiltre peut prendre comme paramètre non seulement une chaîne mais aussi une fonction. De la orderBydocumentation: https://docs.angularjs.org/api/ng/filter/orderBy ):

fonction: fonction Getter. Le résultat de cette fonction sera trié à l'aide de l'opérateur <, =,>.

Ainsi, vous pouvez écrire votre propre fonction. Par exemple, si vous souhaitez comparer des cartes sur la base d'une somme de opt1 et opt2 (j'invente cela, le fait est que vous pouvez avoir n'importe quelle fonction arbitraire), vous écrirez dans votre contrôleur:

$scope.myValueFunction = function(card) {
   return card.values.opt1 + card.values.opt2;
};

puis, dans votre modèle:

ng-repeat="card in cards | orderBy:myValueFunction"

Voici le jsFiddle de travail

L'autre chose à noter est que ce orderByn'est qu'un exemple de filtres AngularJS, donc si vous avez besoin d'un comportement de classement très spécifique, vous pouvez écrire votre propre filtre (bien que cela orderBydevrait suffire pour la plupart des cas d'utilisation).

pkozlowski.opensource
la source
C'est bien, mais est-il possible de créer un filtre pour cela également?
hugo der hungrige
Oui, ça marche toujours. Voici une version mise à jour
jahller
Pourquoi il n'y a pas de description de plusieurs paramètres dans les documents ?? BTW: Merci, cela fonctionne. :)
mayankcpdixit
Savez-vous comment je peux gérer plusieurs critères avec cette approche? Comment puis-je renvoyer plusieurs valeurs à partir de myValueFunction?
akcasoy
26

La solution acceptée ne fonctionne que sur les tableaux, mais pas sur les objets ou les tableaux associatifs. Malheureusement, étant donné qu'Angular dépend de l'implémentation JavaScript de l'énumération des tableaux, l'ordre des propriétés des objets ne peut pas être contrôlé de manière cohérente. Certains navigateurs peuvent parcourir les propriétés des objets de manière lexicographique, mais cela ne peut être garanti.

Par exemple, compte tenu de la mission suivante:

$scope.cards = {
  "card2": {
    values: {
      opt1: 9,
      opt2: 12
    }
  },
  "card1": {
    values: {
      opt1: 9,
      opt2: 11
    }
  }
};

et la directive <ul ng-repeat="(key, card) in cards | orderBy:myValueFunction">, ng-repeat peut itérer sur "card1" avant "card2", quel que soit l'ordre de tri.

Pour contourner ce problème, nous pouvons créer un filtre personnalisé pour convertir l'objet en tableau, puis appliquer une fonction de tri personnalisée avant de renvoyer la collection.

myApp.filter('orderByValue', function () {
  // custom value function for sorting
  function myValueFunction(card) {
    return card.values.opt1 + card.values.opt2;
  }

  return function (obj) {
    var array = [];
    Object.keys(obj).forEach(function (key) {
      // inject key into each object so we can refer to it from the template
      obj[key].name = key;
      array.push(obj[key]);
    });
    // apply a custom sorting function
    array.sort(function (a, b) {
      return myValueFunction(b) - myValueFunction(a);
    });
    return array;
  };
});

Nous ne pouvons pas parcourir les paires (clé, valeur) en conjonction avec des filtres personnalisés (puisque les clés des tableaux sont des index numériques), le modèle doit donc être mis à jour pour référencer les noms de clés injectés.

<ul ng-repeat="card in cards | orderByValue">
    <li>{{card.name}} {{value(card)}}</li>
</ul>

Voici un violon fonctionnel utilisant un filtre personnalisé sur un tableau associatif: http://jsfiddle.net/av1mLpqx/1/

Référence: https://github.com/angular/angular.js/issues/1286#issuecomment-22193332

David
la source
1
Je sais que je ne devrais pas mais je dois juste - Merci.
Elia Weiss
7

Le lien suivant explique très bien les filtres dans Angular. Il montre comment il est possible de définir une logique de tri personnalisée dans un ng-repeat. http://toddmotto.com/everything-about-custom-filters-in-angular-js

Pour trier un objet avec des propriétés, voici le code que j'ai utilisé: (Notez que ce tri est la méthode de tri JavaScript standard et non spécifique à angulaire) Nom de la colonne est le nom de la propriété sur laquelle le tri doit être effectué.

self.myArray.sort(function(itemA, itemB) {
    if (self.sortOrder === "ASC") {
        return itemA[columnName] > itemB[columnName];
    } else {
        return itemA[columnName] < itemB[columnName];
    }
});
Jonathan Cardoz
la source
0

Pour inclure la direction avec la fonction orderBy:

ng-repeat="card in cards | orderBy:myOrderbyFunction():defaultSortDirection"

defaultSortDirection = 0; // 0 = Ascending, 1 = Descending
Ben
la source
emmmmm, juste en passant, je remarque que vous avez écrit à la myOrderbyFunction()place, vous écrirez myOrderbyFunctionsans (), il est appelé pour chaque paire d'éléments afin que vous puissiez fournir un tri personnalisé.
Victor