javascript unknown Como posso agrupar dados com um filtro Angular?




unknown provider: groupbyfilterprovider<- groupbyfilter (7)

Além da resposta aceita, você pode usar isso se quiser agrupar por várias colunas :

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

Eu tenho uma lista de jogadores que pertencem a um grupo cada. Como posso usar um filtro para listar os usuários por grupo?

[{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'}];

Estou procurando este resultado:

  • alfa da equipe
    • Gene
  • equipe beta
    • George
    • Paula
  • equipe gama
    • Steve
    • Scruath do 5º setor

Você pode usar o groupBy do módulo angular.filter .
então você pode fazer algo assim:

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>

RESULTADO:
Nome do grupo: alpha
* jogador: Gene
Nome do grupo: beta
* jogador: George
* jogador: Paula
Nome do Grupo: gama
* jogador: Steve
* jogador: Scruath

ATUALIZAÇÃO: jsbin Lembre-se dos requisitos básicos para usar o angular.filter , note que você deve adicioná-lo às dependências do seu módulo:

(1) Você pode instalar o filtro angular usando 4 métodos diferentes:

  1. clonar e construir este repositório
  2. via Bower: executando $ bower install angular-filter do seu terminal
  3. via npm: executando $ npm, instale o filtro angular do seu terminal
  4. via cdnjs http://www.cdnjs.com/libraries/angular-filter

(2) Inclua o angular-filter.js (ou o angular-filter.min.js) no seu index.html, depois de incluir o próprio Angular.

(3) Adicione 'angular.filter' à lista de dependências do seu módulo principal.


Ambas as respostas foram boas, então eu as movi para uma diretiva para que ela seja reutilizável e uma segunda variável de escopo não precise ser definida.

Aqui está o violino se você quiser vê-lo implementado

Abaixo está a diretiva:

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

Então, ele pode ser usado da seguinte maneira:

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

Atualizar

Inicialmente, escrevi esta resposta porque a versão antiga da solução sugerida por Ariel M., quando combinada com outros $filter s, acionava um " Infite $ diggest Loop Error " ( infdig ) . Felizmente, esse problema foi resolvido na versão mais recente do angular.filter .

Eu sugeri a seguinte implementação, que não teve esse problema :

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

No entanto, essa implementação só funcionará com versões anteriores ao Angular 1.3. (Atualizarei esta resposta em breve, fornecendo uma solução que funcione com todas as versões.)

Na verdade, escrevi um post sobre as etapas que tomei para desenvolver esse $filter , os problemas que encontrei e as coisas que aprendi com ele .


Além das respostas aceitas acima, criei um filtro genérico 'groupBy' usando a biblioteca underscore.js.

JSFiddle (atualizado): http://jsfiddle.net/TD7t3/

O filtro

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

Observe a chamada 'memoize'. Esse método de sublinhado armazena em cache o resultado da função e impede que o ângulo avalie a expressão de filtro todas as vezes, evitando que o angular atinja o limite de iterações de digest.

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

Aplicamos nosso filtro 'groupBy' na variável de escopo teamPlayers, na propriedade 'team'. Nosso ng-repeat recebe uma combinação de (key, values ​​[]) que podemos usar em nossas iterações a seguir.

Atualização de 11 de junho de 2014 Eu expandi o grupo por filtro para considerar o uso de expressões como a chave (por exemplo, variáveis ​​aninhadas). O serviço de análise angular é bastante útil para isso:

O filtro (com suporte de expressão)

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

O controlador (com objetos aninhados)

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

O html (com expressão 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/


Primeiro, faça um loop usando um filtro que retornará apenas equipes exclusivas e, em seguida, um loop aninhado que retornará todos os jogadores por equipe atual:

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>

roteiro:

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

Originalmente usei a resposta do Plantface, mas não gostei de como a sintaxe parecia na minha opinião.

Eu retrabalhei isso para usar $q.defer para pós-processar os dados e retornar uma lista em equipes exclusivas, que é então usada como o filtro.

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

Visão

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

Controlador

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

});




grouping