How do I filter an array by a property that exists in another array?
I have this two arrays in my control:
this.items = [{ "Id": 1, "TypeId": 1, "name": "Michael" },
{ "Id": 2, "TypeId": 2, "name": "Helga" },
{ "Id": 3, "TypeId": 1, "name": "Max" },
{ "Id": 4, "TypeId": 2, "name": "Ann" }];
this.filterBy = [{ "Id": 1, "gender": "Male" },
{ "Id": 2, "gender": "female" }];
And here is my ng-repeat in view template:
<tr ng-repeat="item in list.items">
<td>
<div class="container-fluid">
<div class="row">
<div class="text-center">{{ item.name }}</div>
</div>
</div>
</td>
</tr>
Here is the generated view:
At some point I want to filter items array by gender to show in the view above only females or only males.
I tried to implement filter, like this:
(function () {
"use strict";
angular.module("sensorsData").filter('filterByGender', filterByGender);
function filterByGender() {
var result = [];
return function (items, filterBy) {
if (!items)
return;
if (!filterBy)
return;
angular.forEach(items, function (item) {
angular.forEach(filterBy, function (f) {
if (item.TypeId == f.Id) {
result.push(item);
}
})
});
return result;
}
};
})();
And here how I use it in view template:
<tr ng-repeat="item in list.items | filterByGender:list.filterBy">
<td>
<div class="container-fluid">
<div class="row">
<div class="text-center">{{ item.name }}</div>
</div>
</div>
</td>
</tr>
The desired view in case if content of filterBy array like that:
this.filterBy = [{ "Id": 1, "gender": "Male" }];
The view Should display only items rows with TypeId = 1.
Like this:
But the filter above is not working.
And in the debugger I get this errors:
Error: [ngRepeat:dupes] Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: r in list.arr | filterByAlert:list.filterBy, Duplicate key: object:8, Duplicate value: {"Id":1,"TypeId":1,"name":"Michael"}
http://errors.angularjs.org/1.5.3/ngRepeat/dupes?p0=r%20in%20list.arr%20%7C…%3A8&p2=%7B%22Id%22%3A1%2C%22TypeId%22%3A1%2C%22name%22%3A%22Michael%22%7D
at angular.js:68
at ngRepeatAction (angular.js:28799)
at $watchCollectionAction (angular.js:16734)
at Scope.$digest (angular.js:16869)
at Scope.$apply (angular.js:17133)
at done (angular.js:11454)
at completeRequest (angular.js:11652)
at XMLHttpRequest.requestLoaded (angular.js:11593)
Why is the filter not working? And how can I create a filter to show items by gender?
And this:
angular.js:13424 Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: []
http://errors.angularjs.org/1.5.3/$rootScope/infdig?p0=10&p1=%5B%5D
at angular.js:68
at Scope.$digest (angular.js:16907)
at Scope.$apply (angular.js:17133)
at done (angular.js:11454)
at completeRequest (angular.js:11652)
at XMLHttpRequest.requestLoaded (angular.js:11593)(anonymous function) @ angular.js:13424
angular.js:17136 Uncaught Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: []
http://errors.angularjs.org/1.5.3/$rootScope/infdig?p0=10&p1=%5B%5D
And this:
Uncaught Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: []
Why is the filter not working? And how can I create a filter to show items by gender?
答案 0 :(得分:1)
您可以使用一些非常复杂的过滤功能解决方案,但在我看来,最简单的方法是创建一个合并的项目列表,其中包含您的视图所需的内容(即视图模型)。这样,您只需使用filter
上的ng-repeat
来过滤性别。
这是一个代码段:
var app = angular.module('myapp', []);
app.controller('myctrl', function($scope) {
var items = [{ "Id": 1, "TypeId": 1, "name": "Michael" },
{ "Id": 2, "TypeId": 2, "name": "Helga" },
{ "Id": 3, "TypeId": 1, "name": "Max" },
{ "Id": 4, "TypeId": 2, "name": "Ann" }];
$scope.types = [{ "Id": 1, "gender": "Male" },
{ "Id": 2, "gender": "female" }];
$scope.filterBy = $scope.types[0];
var constructViewModel = function(items, types) {
$scope.mergedItems = [];
angular.forEach(items, function(i){
var mergedItem = angular.copy(i);
var type = types.filter(function(t){return t.Id == i.TypeId})[0];
mergedItem.gender = type.gender;
$scope.mergedItems.push(mergedItem);
});
}
constructViewModel(items, $scope.types);
});
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myapp" ng-controller="myctrl">
<span>Filter by gender: </span><select ng-model="filterBy" ng-options="t as t.gender for t in types"></select>
<div ng-repeat="item in mergedItems | filter: { gender: filterBy.gender } :true">
<td>
<div class="container-fluid">
<div class="row">
<div class="text-center">{{ item.name }} {{item.gender}}</div>
</div>
</div>
</td>
</div>
</div>
&#13;
答案 1 :(得分:1)
问题是您在错误的位置声明result
数组。
function filterByGender() {
var result = [];
return function (items, filterBy) {
...
return result;
}
};
这里的filterByGender
函数是创建过滤函数的函数,而从第3行开始的匿名内部函数是实际的过滤函数。 Angular只调用一次filterByGender
来构建过滤器,然后在每次输入更改时调用第二个函数。在当前代码中,result
数组始终相同(即,在应用程序的生命周期中只存在一个),并且每次调用过滤器时,项目都会再次被推入其中。然后过滤器返回这个完全重复的数组,你从Angular得到错误。
您想要的是每次输入更改时都获得新结果,因此应在匿名函数中声明和初始化results
,而不是filterByGender
。试试这个:
function filterByGender() {
return function (items, filterBy) {
var result = [];
...
return result;
}
};