对象上的角度过滤器可以工作,但会导致Infinite $ digest循环

时间:2016-04-17 11:56:49

标签: 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'"时,过滤器似乎工作正常,但由于某种原因,它被调用太多次并导致Infinite $ digest循环。 任何解决方案??

编辑: 我创建了下面的plunker来显示问题。过滤器可以工作,但在控制台中查找错误。 http://plnkr.co/edit/BXyi75kXT5gkK4E3F5PI

1 个答案:

答案 0 :(得分:2)

您的过滤器会导致无限$ digest循环,因为它总是返回一个新的对象实例。使用相同的参数,它返回一个新的对象实例(如果对象内的数据与之前的数据相同则无关紧要)。

某事导致第二个消化阶段。我猜它是嵌套的ng-repeat。 Angular调用每个摘要阶段的过滤器,因为过滤器返回一个新值,它会导致框架重新评估整个外部ng-repeat块,这会在内部ng-repeat块上产生相同的结果。

选项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循环,但会破坏你的应用程序性能。