我给了一个对象
{
key1: [{...}, {...} ....],
key2: [{...}, {...} ....],
.........so on .....
}
我有一个ng-repeat ng-repeat="(key, values) in data"
,然后在ng-repeat="val in values"
我想根据存储在数组中的对象的某些属性设置过滤器。我已经设置了以下过滤器
.filter('objFilter', function () {
return function (input, search,field) {
if (!input || !search || !field)
return input;
var expected = ('' + search).toLowerCase();
var result = {};
angular.forEach(input, function (value, key) {
result[key] = [];
if(value && value.length !== undefined){
for(var i=0; i<value.length;i++){
var ip = value[i];
var actual = ('' + ip[field]).toLowerCase();
if (actual.indexOf(expected) !== -1) {
result[key].push(value[i]);
}
}
}
});
console.log(result);
return result;
};
当我使用ng-repeat="(date, values) in data| objFilter:search:'url'"
时,过滤器似乎工作正常,但由于某种原因,它被调用太多次并导致Infinite $ digest循环。
任何解决方案??
编辑: 我创建了下面的plunker来显示问题。过滤器可以工作,但在控制台中查找错误。 http://plnkr.co/edit/BXyi75kXT5gkK4E3F5PI
答案 0 :(得分:2)
您的过滤器会导致无限$ digest循环,因为它总是返回一个新的对象实例。使用相同的参数,它返回一个新的对象实例(如果对象内的数据与之前的数据相同则无关紧要)。
某事导致第二个消化阶段。我猜它是嵌套的ng-repeat。 Angular调用每个摘要阶段的过滤器,因为过滤器返回一个新值,它会导致框架重新评估整个外部ng-repeat块,这会在内部ng-repeat块上产生相同的结果。
您可以做的一件事是“稳定”过滤器。如果连续两次调用具有相同值的值,则应返回相同的结果。
使用以下代码替换过滤器:
app.filter('objFilter', function () {
var lastSearch = null;
var lastField = null;
var lastResult = null;
return function (input, search, field) {
if (!input || !search || !field) {
return input;
}
if (search == lastSearch && field == lastField) {
return lastResult;
}
var expected = ('' + search).toLowerCase();
var result = {};
angular.forEach(input, function (value, key) {
result[key] = [];
if(value && value.length !== undefined){
for(var i=0; i<value.length;i++){
var ip = value[i];
var actual = ('' + ip[field]).toLowerCase();
if (actual.indexOf(expected) !== -1) {
//if(result[key]){
result[key].push(value[i]);
//}else{
// result[key] = [value[i]];
//}
}
}
}
});
// Cache params and last result
lastSearch = search;
lastField = field;
lastResult = result;
return result;
};
});
此代码可以正常运行,但它很糟糕且容易出错。它还可能通过将最后提供的参数保留在内存中而导致内存泄漏。
更好的方法是记住模型更改的更新过滤数据。保持JavaScript原样。只更改html:
<body ng-controller="MainCtrl">
<div ng-if="data">
Search:
<input type="text"
ng-model="search"
ng-init="filteredData = (data | objFilter:search:'url')"
ng-change="filteredData = (data | objFilter:search:'url')">
<div ng-repeat="(date, values) in filteredData">
<div style="margin-top:30px;">{{date}}</div>
<hr/>
<div ng-repeat="val in values" class="item">
<div class="h-url">{{val.url}}</div>
</div>
</div>
</div>
</body>
首先,我们添加一个包装器ng-if
,其要求data
必须具有值。这将确保我们的ng-init
具有“数据”,以便设置初始filteredData
值。
我们还将外部ng-repeat更改为使用filteredData
而不是data
。然后,我们使用ng-change
指令更新模型更改的过滤数据。
ng-init
值后data
将触发一次ng-change
现在,无论你有多少个连续的$ digest阶段,过滤器都不会再次触发。它附加在初始化(ng-init
)和用户交互(ng-change
)上。
在每个摘要阶段过滤火灾。作为一般规则,尝试避免直接在ng-repeat上附加复杂的过滤器。
所有这些都将导致带有附加过滤器的ng-repeat重新评估,从而导致子范围创建/销毁和DOM元素操作繁重。它可能不会导致无限的$ digest循环,但会破坏你的应用程序性能。