我给出了一个对象,如下
{
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'"
时,过滤器似乎工作正常但由于某种原因,它被调用太多次并导致无限 $digest 循环。
有什么解决办法吗??
编辑: 我创建了下面的 plunker 来显示这个问题。过滤器有效,但在控制台中查找错误。 http://plnkr.co/edit/BXyi75kXT5gkK4E3F5PI
最佳答案
您的过滤器会导致无限的 $digest 循环,因为它总是返回一个新的对象实例。使用相同的参数,它返回一个新的对象实例(对象内的数据是否与之前相同并不重要)。
某些原因导致第二个消化阶段。我猜这是嵌套的 ng-repeat。 Angular 在每个摘要阶段调用过滤器,并且因为过滤器返回一个新值,它会导致框架重新评估整个外部 ng-repeat block ,这会导致内部 ng-repeat block 发生相同的情况。
选项 1 - 修改过滤器
您可以做的一个修复是“稳定”过滤器。如果连续调用两次相同的值,它应该返回相同的结果。
使用以下代码替换您的过滤器:
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;
};
});
这段代码可以工作,但是很糟糕并且容易出错。由于将最后提供的参数保留在内存中,还可能导致内存泄漏。
选项 2 - 在模型更改时移动过滤器
更好的方法是记住模型更改时更新的过滤数据。让 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-model 的字段的交互都会导致 $digest 阶段
- 每次调用$timeout都会导致$digest阶段(默认)。
- 每次使用 $http 加载内容时,都会开始摘要阶段。
所有这些都会导致带有附加过滤器的 ng-repeat 重新评估,从而导致子作用域创建/销毁和 DOM 元素操作,这是很重的。它可能不会导致无限的 $digest 循环,但会降低您的应用程序性能。
关于angularjs - 对象上的 Angular 过滤器有效,但会导致无限 $digest 循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36676162/