Angular:自定义过滤无限摘要

时间:2014-11-15 04:51:30

标签: javascript angularjs angularjs-ng-repeat

我有一个自定义过滤器,它接受一个Object, subjectBin ,并返回一个全新的Object,结果。过滤器是从 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文档中的一个示例,它显然包含输入并返回一个全新的字符串 out 。它还会抛出任何无限$ digest 错误。在我看来,这与我的过滤器类似,它返回一个全新的Object。

有关我的过滤器为什么会抛出无限$ digest 错误的任何见解,但之后这个其他过滤器没有?

1 个答案:

答案 0 :(得分:7)

在您的代码片段中,您似乎从根本上改变了数据结构。

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

事实上,在它的循环摘要中,Angular足够聪明,能够成功地进行比较:

  • 任何类型的字符串(文字和字符串对象) - "foo" equals new String("foo")

  • 按元素['a', 'b', 'c'] equals new Array(['a', 'b', 'c'])

  • 的1维数组
  • 1个对象的元素foo={a:'a', b:'b'} equals bar={a:'a', b:'b'}

但是,多维数组或对象开始变得混乱。例如,从过滤器中返回一个类似于此['a', 'b', ['c', 'd']]的新数组的新数组将在摘要周期中导致无限循环,因为将oldValueNewValue进行比较始终为false。你可以说Angular执行浅层比较。

总之,在过滤器中返回NEW多维对象或数组将达到无限$ 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']
        }]
    }];

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

此处也是数据重组逻辑的示例( fiddle )。

&#13;
&#13;
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);
    });

  }
]);
&#13;
<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>
&#13;
&#13;
&#13;