Angular:自定义过滤无限摘要

arh*_*ins 3 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;
    };
Run Code Online (Sandbox Code Playgroud)

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

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

  .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;
};
})
Run Code Online (Sandbox Code Playgroud)

这是从角文档的一个例子,它清楚地发生在输入,并返回一个全新的字符串.它也不会引发任何无限$消化错误.在我看来,这与我的过滤器类似,它返回一个全新的Object.

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

Tiv*_*vie 7

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

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

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

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

  • 一维数组由它们的元素组成 ['a', 'b', 'c'] equals new Array(['a', 'b', 'c'])

  • 一维物体的元素 foo={a:'a', b:'b'} equals bar={a:'a', b:'b'}

但是,多维数组或对象开始变得混乱.例如,['a', 'b', ['c', 'd']]从过滤器返回一个像这样的新数组的东西将在摘要周期中导致无限循环,因为oldValue与之相比NewValue总是错误的.你可以说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!']
        }
    };
Run Code Online (Sandbox Code Playgroud)

这是一个难以过滤的结构,因为没有常见的模式(实际上,在你的过滤器中你使用了3个循环!).

您可以重构这样的信息:

var subjectBin = [{
        facultyName: 'faculty1',
        subjects: [{
            subjectName: 'subject1',
            courses: ['math']
        }, {
            subjectName: 'subject2',
            courses: ['science', 'history']
        }]
    }];
Run Code Online (Sandbox Code Playgroud)

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

这里也是数据重构逻辑的一个例子(小提琴).

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);
    });

  }
]);
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)