angularjs - 对象上的 Angular 过滤器有效,但会导致无限 $digest 循环

标签 angularjs angularjs-filter

我给出了一个对象,如下

{
    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/

相关文章:

javascript - 使用 Angular 将字符串中编码的 HTML 转换为输入字段的纯文本

javascript - 当我的某些模型为空或为空时如何重构它

javascript - AngularJS:如何有条件地应用 View 动画?

angularjs - 具有过滤属性的 ng-repeat 过滤器

javascript - AngularJs - 在指令 Controller 中使用自定义过滤器

javascript - firebase 对象大小 : how to ignore properties with $?

angularjs - Chrome 上的 POST 请求 "stall"

angularjs - 如何修复 generator-gulp-angular 的项目,以便进行grunt测试?

javascript - 字体和颜色变化不仅仅适用于日期选择器

javascript - Angularjs ng-repeat 过滤器无法与输入和 ng-model 正常工作