javascript - Angular : custom filter infinite digest

标签 javascript angularjs angularjs-ng-repeat

<分区>

我有一个自定义过滤器,它接收一个对象 subjectBin,并返回一个全新的对象 result。过滤器从 ng-repeat 调用。我的自定义过滤器有效,但会引发无限 $digest 错误。这是我的过滤器:

   return function(subjectBin, field) {

    var result = {},
        faculty,
        subject;

    angular.forEach(subjectBin, function (value, key) {
        faculty = key;
        angular.forEach(value, function (value, key) {
            subject = key;
            value.forEach(function (course) {
                // Check "field" against some of the object's properties
                //
                if (course.asString.toUpperCase().indexOf(field) > -1 ||
                    course.subjectTitle.toUpperCase().indexOf(field) > -1 ||
                    faculty.toUpperCase().indexOf(field) > -1 ) {
                    if (result.hasOwnProperty(faculty)) {
                        if (result[faculty].hasOwnProperty(subject)) {
                            result[faculty][subject].push(course);
                        }
                        else {
                            result[faculty][subject] = [course];
                        }
                    }
                    else {
                        result[faculty] = {};
                        result[faculty][subject] = {};
                        result[faculty][subject] = [course];
                    }
                }
            });
        });
    });

    return result;
    };

根据我的理解,这会产生无限的 $digest 错误,因为每次 $digest 循环发生时我的过滤器都会返回一个全新的对象。这导致脏位被再次设置,一次又一次......

但是当我看到这样的例子时我会感到困惑:

  .filter('reverse', function() {
return function(input, uppercase) {
  input = input || '';
  var out = "";
  for (var i = 0; i < input.length; i++) {
    out = input.charAt(i) + out;
  }
  // conditional based on optional argument
  if (uppercase) {
    out = out.toUpperCase();
  }
  return out;
};
})

这是来自 Angular 文档的示例,它显然接受了input 并返回了一个全新的字符串out。它还不会抛出任何无限$digest错误。在我看来,这类似于我的过滤器,因为它返回一个全新的对象。

关于为什么我的过滤器抛出无限 $digest 错误,但另一个过滤器却没有抛出的任何见解?

最佳答案

在您的代码片段中,您似乎正在彻底改变数据结构。

Angular 过滤器并不意味着对对象进行深度更改。顾名思义,它们是用来过滤信息的。如果您的数据操作要求您更改数据结构,那么您可能做错了。

事实上,在它的循环摘要中,Angular 本身就足够聪明,可以成功比较:

  • 任何类型的字符串(文字和字符串对象)——"foo"等于 new String("foo")

  • 按元素排列的一维数组 ['a', 'b', 'c'] 等于 new Array(['a', 'b', 'c'])

  • 1 维对象的元素 foo={a:'a', b:'b'} 等于 bar={a:'a', b:'b'}

但是,对于多维数组或对象,事情开始变得一团糟。例如,从过滤器返回一个像这样的新数组 ['a', 'b', ['c', 'd']] 将导致摘要循环中的无限循环,因为比较 oldValueNewValue 总是错误的。你可以说 Angular 执行的是浅层比较。

总而言之,在过滤器中返回新的多维对象或数组将达到无限的 $digest 错误。


如果我有一个复杂的多维数据结构,需要进行一些深层次的比较怎么办?

如果您不在每个循环中创建新对象,那么没有什么能阻止您进行深入比较。但是,数据的结构应该正确。

例如,在您的情况下,您似乎正在使用对象作为键映射,如下所示:

var subjectBin = {
        faculty1: {
            subject1: ['math'],
            subject2: ['science', 'history'],
            subject3: ['foo', 'blabla'],
            subject4: ['unraveling', 'the mistery']
        },
        faculty2: {
            subject1: ['that', 'all'],
            subject2: ['started', 'with a'],
            subject3: ['foo', 'blabla'],
            subject4: ['bigbang', 'BANG!']
        }
    };

这是一个很难过滤的结构,因为没有通用模式(事实上,在您的过滤器中您使用了 3 个循环!!!)。

您可以像这样重组信息:

var subjectBin = [{
        facultyName: 'faculty1',
        subjects: [{
            subjectName: 'subject1',
            courses: ['math']
        }, {
            subjectName: 'subject2',
            courses: ['science', 'history']
        }]
    }];

这会更容易使用。事实上,你甚至不需要自定义过滤器,你可以使用默认的 Angular 过滤器进行简单的比较

这里还有一个数据重组逻辑的例子( fiddle )。

var app = angular.module('app', []);

app.controller('fooCtrl', ['$scope',
  function($scope) {
    var bla = {
      faculty1: {
        subject1: ['math'],
        subject2: ['science', 'history'],
        subject3: ['foo', 'blabla'],
        subject4: ['unraveling', 'the mistery']
      },
      faculty2: {
        subject1: ['that', 'all'],
        subject2: ['started', 'with a'],
        subject3: ['foo', 'blabla'],
        subject4: ['bigbang', 'BANG!']
      }
    };


    $scope.items = [];

    angular.forEach(bla, function(value, key) {
      var faculty = {
        name: key,
        subjects: []
      };

      angular.forEach(value, function(value, key) {
        var subject = {
          name: key,
          courses: value
        };
        faculty.subjects.push(subject);
      });
      $scope.items.push(faculty);
    });

  }
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='app'>
  <div ng-controller="fooCtrl">
    <ul>
      <li ng-repeat="faculty in items">Faculty Name: {{faculty.name}}
        <br/>
        <ul>
          <li ng-repeat="subject in faculty.subjects | filter:{courses:['foo']}">Subject name: {{subject.name}}
            <br/>
            <ul>
              <li ng-repeat="course in subject.courses">{{course}}</li>
            </ul>
          </li>
        </ul>Subjects: {{item.subjects}}</li>
    </ul>
  </div>

关于javascript - Angular : custom filter infinite digest,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26942674/

相关文章:

javascript - 将本地存储重构为 PouchDB

javascript - AngularJS ng-repeat 不起作用

javascript - 如何在使用 ng-options 创建的 html 下拉列表中设置默认值

javascript - 我如何在 extjs 中访问这个内部项目

javascript - 单击其他菜单项时隐藏当前子菜单

javascript - 使用 $setValidity ("required",false) 按要求设置后,AngularJS 文本框未更改为有效

angularjs - 在 ng-repeat 中创建父 div

php - 什么是JS等价于PHP函数number_format?

javascript - 找不到变量 : StyleSheet

angularjs - 如何防止 Angular 指令之间共享作用域?