Как я могу группировать данные с помощью фильтра Angular?
У меня есть список игроков, которые принадлежат каждой группе. Как я могу использовать фильтр для перечисления пользователей на группу?
[{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-го сектора
Ответы
Ответ 1
Вы можете использовать groupBy модуля angular.filter.
поэтому вы можете сделать что-то вроде этого:
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>
РЕЗУЛЬТАТ:
Название группы: alpha
* игрок: Gene
Название группы: бета
* игрок: George
* игрок: Паула
Название группы: gamma
* игрок: Стив
* игрок: Scruath
ОБНОВЛЕНИЕ: jsbin Помните основные требования к использованию angular.filter
, особенно обратите внимание, что вы должны добавить его к вашим зависимостям модулей:
(1) Вы можете установить angular -фильтр, используя 4 разных метода:
- клонировать и создавать этот репозиторий
- через Bower: запустив $bower install angular -фильтр с вашего терминала
- через npm: запустив $npm install angular -фильтр с вашего терминала
- через cdnjs http://www.cdnjs.com/libraries/angular-filter
(2) Включите angular -filter.js(или angular -filter.min.js) в свой index.html, включив Angular сам.
(3) Добавьте 'angular.filter' в список основных зависимостей вашего модуля.
Ответ 2
В дополнение к принятым ответам выше я создал общий фильтр "groupBy", используя библиотеку underscore.js.
JSFiddle (обновлено):
http://jsfiddle.net/TD7t3/
Фильтр
app.filter('groupBy', function() {
return _.memoize(function(items, field) {
return _.groupBy(items, field);
}
);
});
Обратите внимание на вызов memoize. Этот метод подчеркивания кэширует результат функции и останавливает angular от оценки выражения фильтра каждый раз, тем самым предотвращая достижение angular предела итераций дайджеста.
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>
Мы применяем наш фильтр "groupBy" в переменной scope teamPlayers в свойстве "team". Наш ng-repeat получает комбинацию (ключ, значения []), которую мы можем использовать в следующих итерациях.
Обновление 11 июня 2014 года
Я развернул группу по фильтру для учета использования выражений в качестве ключа (например, вложенных переменных). Сервис parse angular подходит для этого:
Фильтр (с поддержкой выражения)
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/
Ответ 3
Сначала сделайте цикл, используя фильтр, который будет возвращать только уникальные команды, а затем вложенный цикл, который возвращает всех игроков в текущей команде:
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>
script:
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;
}
}
Ответ 4
Я первоначально использовал ответ 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);
});
Ответ 5
Оба ответа были хорошими, поэтому я переместил их в директиву, чтобы он был повторно использован, и вторая переменная области видимости не должна быть определена.
Вот скрипка, если вы хотите увидеть ее реализовано
Ниже приведена директива:
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>
Ответ 6
Update
Я изначально написал этот ответ, потому что старая версия решения, предложенная Ariel M. в сочетании с другими $filter
s, вызвала "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
, о проблемах, с которыми я столкнулся, и о том, что я узнал из него,
Ответ 7
В дополнение к принятому ответу вы можете использовать это, если хотите группировать несколько столбцов:
<ul ng-repeat="(key, value) in players | groupBy: '[team,name]'">
Ответ 8
Если вам это нужно в коде JS. Вы можете использовать встроенный метод angula-filter lib. Как это.
function controller($scope, $http, groupByFilter) {
var groupedData = groupByFilter(originalArray, 'groupPropName');
}
https://github.com/a8m/angular-filter/wiki/Common-Questions#inject-filters