각도 필터로 데이터를 그룹화하려면 어떻게해야합니까?


136

나는 그룹에 속하는 선수 목록을 가지고 있습니다. 필터를 사용하여 그룹당 사용자를 나열하려면 어떻게해야합니까?

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

이 결과를 찾고 있습니다.

  • 팀 알파
    • 유전자
  • 팀 베타
    • 성 조지
    • 폴라
  • 팀 감마
    • 스티브
    • 5 구간 정찰

답변:


182

angular.filter 모듈 의 groupBy를 사용할 수 있습니다 .
그래서 당신은 이런 식으로 할 수 있습니다 :

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>

결과 :
그룹 이름 : 알파
* 플레이어 : 유전자
그룹 이름 : 베타
* 플레이어 : George
* 플레이어 : Paula
그룹 이름 : 감마
* 플레이어 : Steve
* 플레이어 : Scruath

업데이트 : jsbin 사용할 기본 요구 사항을 기억하십시오. angular.filter특히 모듈의 종속성에 추가해야합니다.

(1) 4 가지 방법으로 각도 필터를 설치할 수 있습니다.

  1. 이 저장소를 복제하고 빌드하십시오.
  2. Bower를 통해 : 터미널에서 $ bower install angular-filter를 실행하여
  3. npm을 통해 : 터미널에서 $ npm install angular-filter를 실행하여
  4. cdnjs를 통해 http://www.cdnjs.com/libraries/angular-filter

(2) Angular 자체를 포함시킨 후 index.html에 angular-filter.js (또는 angular-filter.min.js)를 포함시킵니다.

(3) 메인 모듈의 의존성 목록에 'angular.filter'를 추가하십시오.


좋은 예입니다. 그러나 키는 실제 키가 아닌 그룹 이름을 반환합니다. 어떻게 해결할 수 있습니까?
JohnAndrews

7
angular.filter모듈 을 포함하는 것을 잊지 마십시오 .
Puce

1
당신은 @erfling 그룹에 의해에 의해 순서를-사용할 수 PTAL에 : github.com/a8m/angular-filter/wiki/...
a8m

1
오 와우. 감사. 그런 식으로 외부 루프에 영향을 미치기 위해 중첩 루프를 주문하는 것을 기대하지 않았습니다. 정말 유용합니다. +1
erfling

1
@ Xyroid 심지어 key객체 로 만들고 싶은 것과 똑같은 것을 찾고 있습니다. 당신에게서 어떤 행운
슈퍼 쿨

25

위의 허용 된 답변 외에도 underscore.js 라이브러리를 사용하여 일반 'groupBy'필터를 만들었습니다.

JSFiddle (업데이트 됨) : http://jsfiddle.net/TD7t3/

필터

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

'memoize'호출에 유의하십시오. 이 밑줄 방법은 함수의 결과를 캐시하고 각도가 필터 표현을 평가할 때마다 각도를 중지하여 각도가 다이제스트 반복 한계에 도달하지 못하게합니다.

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>

'team'속성의 teamPlayers 범위 변수에 'groupBy'필터를 적용합니다. ng-repeat는 다음 반복에서 사용할 수있는 (키, 값 []) 조합을받습니다.

2014 년 6 월 11 일 업데이트 저는 식을 키로 사용하는 것을 설명하기 위해 필터를 기준으로 그룹을 확장했습니다 (예 : 중첩 변수). 각도 파싱 서비스는 다음과 같이 매우 편리합니다.

필터 (표현식 지원)

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

컨트롤러 (중첩 된 객체 포함)

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

html (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/


당신은 맞습니다, 바이올린 링크가 업데이트되었습니다. 알려 주셔서 감사합니다.
chrisv

3
이것은 실제로 매우 깔끔합니다! 최소한의 코드.
베니 보 테마

3
기본적으로 memoize는 첫 번째 매개 변수 (예 : '항목')를 캐시 키로 사용하므로 다른 '필드'와 동일한 '항목'을 전달하면 동일한 캐시 된 값을 반환합니다. 솔루션 환영합니다.
Tom Carver

나는 당신이이 문제를 얻기 위해 $ ID 값을 사용할 수 있습니다 생각 : 항목의 항목이 $ id로 (항목) 추적
캐스퍼 하머을

2
어떤 "허용 된 답변"? 스택 오버플로에서는 허용되는 답변이 하나만있을 수 있습니다.
Sebastian Mach

19

먼저 고유 한 팀만 반환하는 필터를 사용하여 루프를 수행 한 다음 현재 팀당 모든 플레이어를 반환하는 중첩 루프를 사용하십시오.

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>

스크립트:

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

너무 간단합니다. 멋진 @Plantface.
Jeff Yates

그냥 화려한. 그러나 클릭 할 때 새 객체를 $ scope.players에 푸시하려면 어떻게해야합니까? u가 함수를 반복하면서 추가 될까요?
슈퍼 쿨

16

나는 원래 Plantface의 대답을 사용했지만 구문이 내 관점에서 어떻게 보이는지 좋아하지 않았습니다.

$ q.defer 를 사용 하여 데이터를 사후 처리하고 고유 한 팀에 대한 목록을 반환하도록 필터를 수정했습니다.

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

전망

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

제어 장치

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

});

1
이 답변은 AngularJS> 1.1에서 작동하지 않습니다. Promised는 더 이상 배열에서 래핑되지 않습니다. 참고 항목 이민 노트
베니 Bottema

6
비동기 적으로 작업을 수행하지 않으므로이 솔루션에서 약속이 필요하지 않습니다. 이 경우 간단히 해당 단계 ( jsFiddle ) 를 건너 뛸 수 있습니다 .
베니 보 테마

11

두 대답 모두 좋았으므로 재사용 가능하고 두 번째 범위 변수를 정의 할 필요가 없도록 지시문으로 옮겼습니다.

여기는 바이올린입니다그것이 구현되고 싶다면 있습니다.

아래는 지시어입니다.

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

그런 다음 다음과 같이 사용할 수 있습니다.

<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

최신 정보

다른 답변 과 결합 될 때$filter Ariel M.이 제안한 구 버전의 솔루션 이 " Infite $ diggest Loop Error "( infdig)를 유발 했기 때문에 처음에이 답변을 썼습니다 . 다행히도이 문제는 최신 버전의 angular.filter 에서 해결되었습니다 .

나는 그 문제가없는 다음 구현을 제안했다 .

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

그러나이 구현은 Angular 1.3 이전 버전에서만 작동합니다. (모든 버전에서 작동하는 솔루션을 제공하기 위해이 답변을 곧 업데이트하겠습니다.)

나는 실제로 이것을 개발하기 위해 취한 단계들 $filter, 내가 겪은 문제들 그리고 내가 배운 것들 에 관한 글을 썼다 .


안녕하세요 @Josep, 새로운 angular-filter버전 -0.5.0을 살펴보십시오 . 더 이상 예외는 없습니다. groupBy어떤 필터와도 체인이 될 수 있습니다. 또한, 당신이있는 거 좋은 테스트 케이스가 성공적으로 완료 - 여기의 plunker에 감사합니다.
a8m

1
@Josep Angular 1.3에 문제가 있음
amcdnl

2

허용 된 답변 외에도 여러 열로 그룹화하려는 경우 이것을 사용할 수 있습니다 .

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.