web-dev-qa-db-fra.com

Comment regrouper des données avec un filtre angulaire?

J'ai une liste de joueurs qui appartiennent à un groupe chacun. Comment utiliser un filtre pour répertorier les utilisateurs par groupe?

[{name: 'Gene', team: 'team alpha'},
 {name: 'George', team: 'team beta'},
 {name: 'Steve', team: 'team gamma'},
 {name: 'Paula', team: 'team beta'},
 {name: 'Scruath of the 5th sector', team: 'team gamma'}];

Je cherche ce résultat:

  • équipe alpha
    • Gène
  • équipe bêta
    • George
    • Paula
  • équipe gamma
    • Steve
    • Scruath du 5ème secteur
128
Benny Bottema

Vous pouvez utiliser groupBy of angular.filter module.
afin que vous puissiez faire quelque chose comme ceci:

JS:

$scope.players = [
  {name: 'Gene', team: 'alpha'},
  {name: 'George', team: 'beta'},
  {name: 'Steve', team: 'gamma'},
  {name: 'Paula', team: 'beta'},
  {name: 'Scruath', team: 'gamma'}
];

HTML:

<ul ng-repeat="(key, value) in players | groupBy: 'team'">
  Group name: {{ key }}
  <li ng-repeat="player in value">
    player: {{ player.name }} 
  </li>
</ul>

RÉSULTAT:
Nom du groupe: alpha
* joueur: Gene
Nom du groupe: beta
* joueur: George
* joueur: Paula
Nom du groupe: gamma
* joueur: Steve
* joueur: Scruath

UPDATE:jsbin N'oubliez pas les exigences de base pour utiliser angular.filter, en particulier vous devez l'ajouter aux dépendances de votre module

(1) Vous pouvez installer angular-filter en utilisant 4 méthodes différentes:

  1. cloner et construire ce dépôt
  2. via Bower: en exécutant $ bower, installez angular-filter depuis votre terminal
  3. via npm: en exécutant $ npm, installez angular-filter depuis votre terminal
  4. via cdnjs http://www.cdnjs.com/libraries/angular-filter

(2) Incluez angular-filter.js (ou angular-filter.min.js) dans votre index.html, après avoir inclus Angular lui-même.

(3) Ajoutez 'angular.filter' à la liste des dépendances de votre module principal.

172
a8m

En plus des réponses acceptées ci-dessus, j'ai créé un filtre générique 'groupBy' à l'aide de la bibliothèque underscore.js.

JSFiddle (mise à jour): http://jsfiddle.net/TD7t3/

Le filtre

app.filter('groupBy', function() {
    return _.memoize(function(items, field) {
            return _.groupBy(items, field);
        }
    );
});

Notez l'appel 'memoize'. Cette méthode de soulignement met en cache le résultat de la fonction et empêche angular d'évaluer l'expression du filtre à chaque fois, empêchant ainsi angular d'atteindre la limite d'itérations de digestion.

Le html

<ul>
    <li ng-repeat="(team, players) in teamPlayers | groupBy:'team'">
        {{team}}
        <ul>
            <li ng-repeat="player in players">
                {{player.name}}
            </li>
        </ul>
    </li>
</ul>

Nous appliquons notre filtre 'groupBy' sur la variable d'étendue teamPlayers, sur la propriété 'team'. Notre ng-repeat reçoit une combinaison de (clé, valeurs []) que nous pouvons utiliser dans nos itérations suivantes.

_ {Mise à jour le 11 juin 2014} _ J'ai développé le filtre de groupe par pour rendre compte de l'utilisation d'expressions en tant que clé (par exemple des variables imbriquées). Le service d'analyse angulaire est très pratique pour cela:

Le filtre (avec support d'expression)}

app.filter('groupBy', function($parse) {
    return _.memoize(function(items, field) {
        var getter = $parse(field);
        return _.groupBy(items, function(item) {
            return getter(item);
        });
    });
});

Le contrôleur (avec les objets imbriqués)

app.controller('homeCtrl', function($scope) {
    var teamAlpha = {name: 'team alpha'};
    var teamBeta = {name: 'team beta'};
    var teamGamma = {name: 'team gamma'};

    $scope.teamPlayers = [{name: 'Gene', team: teamAlpha},
                      {name: 'George', team: teamBeta},
                      {name: 'Steve', team: teamGamma},
                      {name: 'Paula', team: teamBeta},
                      {name: 'Scruath of the 5th sector', team: teamGamma}];
});

Le code HTML (avec l'expression sortBy)}

<li ng-repeat="(team, players) in teamPlayers | groupBy:'team.name'">
    {{team}}
    <ul>
        <li ng-repeat="player in players">
            {{player.name}}
        </li>
    </ul>
</li>

JSFiddle: http://jsfiddle.net/k7fgB/2/

24
chrisv

Commencez par faire une boucle en utilisant un filtre qui ne renvoie que des équipes uniques, puis une boucle imbriquée qui renvoie tous les joueurs par équipe actuelle:

http://jsfiddle.net/plantface/L6cQN/

html:

<div ng-app ng-controller="Main">
    <div ng-repeat="playerPerTeam in playersToFilter() | filter:filterTeams">
        <b>{{playerPerTeam.team}}</b>
        <li ng-repeat="player in players | filter:{team: playerPerTeam.team}">{{player.name}}</li>        
    </div>
</div>

scénario:

function Main($scope) {
    $scope.players = [{name: 'Gene', team: 'team alpha'},
                    {name: 'George', team: 'team beta'},
                    {name: 'Steve', team: 'team gamma'},
                    {name: 'Paula', team: 'team beta'},
                    {name: 'Scruath of the 5th sector', team: 'team gamma'}];

    var indexedTeams = [];

    // this will reset the list of indexed teams each time the list is rendered again
    $scope.playersToFilter = function() {
        indexedTeams = [];
        return $scope.players;
    }

    $scope.filterTeams = function(player) {
        var teamIsNew = indexedTeams.indexOf(player.team) == -1;
        if (teamIsNew) {
            indexedTeams.Push(player.team);
        }
        return teamIsNew;
    }
}
19
Benny Bottema

A l'origine, j'avais utilisé la réponse de Plantface, mais je n'aimais pas la syntaxe à mon avis.

Je l'ai retravaillé pour utiliser $ q.defer afin de post-traiter les données et de renvoyer une liste sur des équipes uniques, qui est ensuite utilisée comme filtre.

http://plnkr.co/edit/waWv1donzEMdsNMlMHBa?p=preview

Vue

<ul>
  <li ng-repeat="team in teams">{{team}}
    <ul>
      <li ng-repeat="player in players | filter: {team: team}">{{player.name}}</li> 
    </ul>
  </li>
</ul>

Manette

app.controller('MainCtrl', function($scope, $q) {

  $scope.players = []; // omitted from SO for brevity

  // create a deferred object to be resolved later
  var teamsDeferred = $q.defer();

  // return a promise. The promise says, "I promise that I'll give you your
  // data as soon as I have it (which is when I am resolved)".
  $scope.teams = teamsDeferred.promise;

  // create a list of unique teams. unique() definition omitted from SO for brevity
  var uniqueTeams = unique($scope.players, 'team');

  // resolve the deferred object with the unique teams
  // this will trigger an update on the view
  teamsDeferred.resolve(uniqueTeams);

});
16
Walter Stabosz

Les deux réponses étaient bonnes, alors je les ai déplacées dans une directive afin qu'elle soit réutilisable et qu'une deuxième variable de portée ne soit pas définie.

Voici le violon si vous voulez le voir implémenté

Ci-dessous la directive:

var uniqueItems = function (data, key) {
    var result = [];
    for (var i = 0; i < data.length; i++) {
        var value = data[i][key];
        if (result.indexOf(value) == -1) {
            result.Push(value);
        }
    }
    return result;
};

myApp.filter('groupBy',
            function () {
                return function (collection, key) {
                    if (collection === null) return;
                    return uniqueItems(collection, key);
        };
    });

Ensuite, il peut être utilisé comme suit:

<div ng-repeat="team in players|groupBy:'team'">
    <b>{{team}}</b>
    <li ng-repeat="player in players | filter: {team: team}">{{player.name}}</li>        
</div>
11
Theo

Mettre à jour

J'ai initialement écrit cette réponse car l'ancienne version de la solution suggérée par Ariel M. lorsqu'elle était combinée avec d'autres $filters déclenchait une "Infite $ diggest Loop Error" (infdig) . Heureusement, ce problème a été résolu dans la dernière version du angular.filter

J'ai suggéré l'implémentation suivante, qui n'avait pas ce problème :

angular.module("sbrpr.filters", [])
.filter('groupBy', function () {
  var results={};
    return function (data, key) {
        if (!(data && key)) return;
        var result;
        if(!this.$id){
            result={};
        }else{
            var scopeId = this.$id;
            if(!results[scopeId]){
                results[scopeId]={};
                this.$on("$destroy", function() {
                    delete results[scopeId];
                });
            }
            result = results[scopeId];
        }

        for(var groupKey in result)
          result[groupKey].splice(0,result[groupKey].length);

        for (var i=0; i<data.length; i++) {
            if (!result[data[i][key]])
                result[data[i][key]]=[];
            result[data[i][key]].Push(data[i]);
        }

        var keys = Object.keys(result);
        for(var k=0; k<keys.length; k++){
          if(result[keys[k]].length===0)
            delete result[keys[k]];
        }
        return result;
    };
});

Cependant, cette implémentation ne fonctionnera qu'avec les versions antérieures à Angular 1.3. (Je mettrai à jour cette réponse prochainement en fournissant une solution qui fonctionne avec toutes les versions.)

J'ai en fait écrit un article sur les étapes que j'ai suivies pour développer ce $filter, les problèmes que j'ai rencontrés et les choses que j'ai apprises de celui-ci }. 

11
Josep

En plus de la réponse acceptée, vous pouvez l'utiliser si vous souhaitez grouper par plusieurs colonnes :

<ul ng-repeat="(key, value) in players | groupBy: '[team,name]'">
1
Luis Teijon

Si vous avez besoin de ça en code js. Vous pouvez utiliser la méthode injectée de angula-filter lib. Comme ça.

function controller($scope, $http, groupByFilter) {     

   var groupedData = groupByFilter(originalArray, 'groupPropName');

}

https://github.com/a8m/angular-filter/wiki/Common-Questions#inject-filters

0
trueboroda