对函数的指令绑定最终在$ digest循环中 - 错误:$ rootScope:infdig

时间:2017-07-07 15:50:01

标签: angularjs angularjs-directive

我想要一个与对象列表一起使用的指令,但我还需要接受双向绑定或函数绑定。

app.directive('myDir', function() {
    return {
        scope: {
            list: "=?",
            list_func: "&?listFunc"
        },

        controller: ['$scope', function($scope) {
            $scope.get_list = function() {
                if($scope.list !== undefined)
                    return $scope.list();

                if($scope.list_func !== undefined)
                    return $scope.list_func()();
            };
        }]
    };
});

但是,当我使用带有返回列表的函数的listFunc属性时,我收到此错误:

  

VM607 angular.js:68 Uncaught Error:[$ rootScope:infdig] 10 $ digest()迭代次数达到。中止!       观察者在最后5次迭代中被解雇:[[{“msg”:“fn:regularInterceptedExpression”,“newVal”:19,“oldVal”:17},{“msg”:“fn:regularInterceptedExpression”,“newVal”:“ Harry“},{”msg“:”fn:regularInterceptedExpression“,”newVal“:”65“},{”msg“:”fn:regularInterceptedExpression“,”newVal“:”Sally“},{”msg“:” fn:regularInterceptedExpression“,”newVal“:”66“},{”msg“:”fn:regularInterceptedExpression“,”newVal“:9,”oldVal“:8},{”msg“:”fn:regularInterceptedExpression“,” newVal“:”val“},{”msg“:”fn:regularInterceptedExpression“,”newVal“:”1“}],[{”msg“:”fn:regularInterceptedExpression“,”newVal“:21,”oldVal“ :19},{“msg”:“fn:regularInterceptedExpression”,“newVal”:“Harry”},{“msg”:“fn:regularInterceptedExpression”,“newVal”:“65”},{“msg”:“ fn:regularInterceptedExpression“,”newVal“:”Sally“},{”msg“:”fn:regularInterceptedExpression“,”newVal“:”66“},{”msg“:”fn:regularInterceptedExpression“,”newVal“:10 ,“oldVal”:9},{“msg”:“fn:regularInterceptedExpression”,“newVal”:“val”},{“msg”:“fn:regularInterceptedExpression”,“newVal” :“1”}],[{“msg”:“fn:regularInterceptedExpression”,“newVal”:23,“oldVal”:21},{“msg”:“fn:regularInterceptedExpression”,“newVal”:“Harry” },{“msg”:“fn:regularInterceptedExpression”,“newVal”:“65”},{“msg”:“fn:regularInterceptedExpression”,“newVal”:“Sally”},{“msg”:“fn: regularInterceptedExpression“,”newVal“:”66“},{”msg“:”fn:regularInterceptedExpression“,”newVal“:11,”oldVal“:10},{”msg“:”fn:regularInterceptedExpression“,”newVal“ :“val”},{“msg”:“fn:regularInterceptedExpression”,“newVal”:“1”}],[{“msg”:“fn:regularInterceptedExpression”,“newVal”:25,“oldVal”:23 },{“msg”:“fn:regularInterceptedExpression”,“newVal”:“Harry”},{“msg”:“fn:regularInterceptedExpression”,“newVal”:“65”},{“msg”:“fn: regularInterceptedExpression“,”newVal“:”Sally“},{”msg“:”fn:regularInterceptedExpression“,”newVal“:”66“},{”msg“:”fn:regularInterceptedExpression“,”newVal“:12,” oldVal“:11},{”msg“:”fn:regularInterceptedExpression“,”newVal“:”val“},{”msg“:”fn:regularInterceptedExpression“,”newVal“:”1“}],[{” msg“:”fn:regularInter ceptedExpression“,”newVal“:27,”oldVal“:25},{”msg“:”fn:regularInterceptedExpression“,”newVal“:”Harry“},{”msg“:”fn:regularInterceptedExpression“,”newVal“ :“65”},{“msg”:“fn:regularInterceptedExpression”,“newVal”:“Sally”},{“msg”:“fn:regularInterceptedExpression”,“newVal”:“66”},{“msg” :“fn:regularInterceptedExpression”,“newVal”:13,“oldVal”:12},{“msg”:“fn:regularInterceptedExpression”,“newVal”:“val”},{“msg”:“fn:regularInterceptedExpression” “的newval”: “1”}]]       http://errors.angularjs.org/1.6.2/$rootScope/infdig?p0=10&p1=%5B%5B%7B%22ms…2fn%3A%20regularInterceptedExpression%22%2C%22newVal%22%3A%221%22%7D%5D%5D           在VM607 angular.js:68           在Scope。$ digest(VM607 angular.js:17893)           在Scope。$ apply(VM607 angular.js:18125)           完成后(VM607 angular.js:12233)           at completeRequest(VM607 angular.js:12459)           在XMLHttpRequest.requestLoaded(VM607 angular.js:12387)

我创建了this plunker示例(打开一个javascript控制台,你可以看到这些错误),展示我需要的东西。在这个例子中,我得到了这些错误,但视图仍然得到更新。在我正在开发的应用程序(更大)上,我收到了很多$digest错误,导致网站速度变慢,视图也无法更新。

在无限循环中结束函数的最佳方法是什么?

1 个答案:

答案 0 :(得分:2)

Angular在get_list()表达式上注册隐式$watchCollection。此表达式将被调用至少两次,因为angular想要检查模型是否已稳定。

<ul>
  <li ng-repeat="obj in get_list()">
    name: {{ obj.name }}<br />
    age: {{ obj.age }}
  </li>
</ul>

每次调用get_list()时,您的代码都会返回不同的对象数组,因此angular会继续消化,直到达到最大摘要周期限制。

  $scope.get_list = function(val) {
    return function() {
      return [ { name: "val", age: val } ];
    }
  }

即使内容相同,该函数每次调用时都会创建新对象。

避免在模板中使用函数调用。而是将值放在范围上,让指令对这些值的变化做出反应。

来自文档:

  

错误:$ rootScope:infdig

     

当应用程序的模型变得不稳定并且每个$digest周期触发状态更改和后续$digest周期时,会发生此错误。 AngularJS检测到这种情况并防止无限循环导致浏览器无响应。

     

一个常见错误是绑定到每次调用时生成新数组的函数。例如:

<div ng-repeat="user in getUsers()">{{ user.name }}</div>
     
$scope.getUsers = function() {
    return [ { name: 'Hank' }, { name: 'Francisco' } ];
};
     

由于getUsers()返回一个新数组,AngularJS确定每个$digest周期的模型不同,从而导致错误。如果元素没有改变,解决方案是返回相同的数组对象:

var users = [ { name: 'Hank' }, { name: 'Francisco' } ];

$scope.getUsers = function() {
    return users;
};
     

— AngularJS Error Reference - Error: $rootScope:infdig

  

我应该放弃这种功能并使用过滤器吗?

TL; DR是的。

组件应该充当“皮肤”。它们应该只显示给它们的数据并将用户事件传递给父控制器。模型应该是Single Source of Truth。指令不需要从父控制器获取数据。

表达式&绑定应该仅用于将用户事件传递给父控制器。然后,父控制器应该适当地更改模型。这个Separation of Concerns使得应用程序更易于理解,调试,测试和维护。

  

还有一个问题:这也适用于返回函数的函数,对吗?

应避免使用“函数返回函数”模式。这使得一个令人困惑的模板。而是使用locals调用函数:

<my-component on-event="gotEvent($event)">
</my-compnent>
scope: { onEvent: "&" },
template: `<input ng-model=myModel ng-change="myChange()" />`,
link: function(scope, elem, attrs) {
    scope.myChange = function() {
        scope.onEvent({$event: myModel});
    };
} 

我建议在$前加上局部变量,以区别于父范围变量。有些人建议使用$event来符合Angular 2 +的惯例。

来自文档:

  
      
  • &&attr - 提供了在父作用域的上下文中执行表达式的方法。如果未指定attr名称,则假定属性名称与本地名称相同。给定<my-component my-attr="count = count + $value">和隔离范围定义scope: { localFn:'&myAttr' },隔离范围属性localFn将指向count = count + $value表达式的函数包装器。通常需要通过表达式将数据从隔离范围传递到父范围。这可以通过将局部变量名称和值的映射传递到表达式包装器fn来完成。例如,如果表达式为increment($amount),那么我们可以通过将localFn称为localFn({$amount: 22})来指定金额值。
  •   
     

— AngularJS Comprehensive Directive API Reference ($scope)