【问题标题】:Directive binding to functions end up in $digest loop -- Error: $rootScope:infdig与函数的指令绑定最终出现在 $digest 循环中——错误:$rootScope:infdig
【发布时间】:2017-12-12 00:02:13
【问题描述】:

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

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 未捕获的错误:[$rootScope:infdig] 达到了 10 个 $digest() 迭代。中止! 观察者在最近 5 次迭代中触发:[[{"msg":"fn:regularInterceptedExpression","newVal":19,"oldVal":17},{"msg":"fn:regularInterceptedExpression","newVal":"哈利"},{"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: 常规间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) 在 completeRequest (VM607 angular.js:12459) 在 XMLHttpRequest.requestLoaded (VM607 angular.js:12387)

我创建了 this plunker 示例(打开一个 javascript 控制台,您可以看到这些错误)演示我需要什么。在此示例中,我收到了这些错误,但视图仍会更新。在我正在开发的应用程序(更大)上,我收到很多$digest 错误,导致网站速度变慢并且视图没有更新。

在不以无限循环结束的情况下绑定函数的最佳方法是什么?

【问题讨论】:

  • 表达式& 绑定只能用于将事件传递给父控制器。使用一种方式< 从父控制器获取数据。避免双向= 绑定。
  • @georgeawg 感谢您的评论。我尽可能避免双向绑定,在我的例子中,输入列表会根据用户与指令的交互而略有变化。

标签: angularjs angularjs-directive


【解决方案1】:

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。指令不需要从父控制器获取数据。

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


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

应该避免“函数返回函数”模式。它造成了一个令人困惑的模板。而是使用本地调用函数:

<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+ 的约定。

来自文档:

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

— AngularJS Comprehensive Directive API Reference ($scope)

【讨论】:

  • 谢谢你的解释,我应该知道的,我想我没有足够仔细地关注。
  • 还有一个问题:这也适用于返回函数的函数,对吧?我修改了我的 plunker (plnkr.co/edit/OxuCZZgf5i06SuOU9LYl?p=preview) 以便 get_peeps 返回相同的对象。但是get_peeps 实际上返回了一个返回相同对象的函数。现在我没有收到任何错误,但是如果我在模板中使用不同的参数 &lt;my-dir list-func="get_peeps(1)"&gt;&lt;/my-dir&gt; &lt;my-dir list-func="get_peeps(2)"&gt;&lt;/my-dir&gt; 复制我的 html 代码,那么我会再次得到 $rootScope:infdig。两个指令有不同的作用域,为什么我现在得到这个错误?
  • 我应该放弃这种功能而使用过滤器吗?
  • 我遇到了同样的问题,我遵循了这个链接上的建议,它成功了!:[在 Angular 中构建嵌套递归指令][1] [1]:sporto.github.io/blog/2013/06/24/…
猜你喜欢
  • 1970-01-01
  • 2023-03-24
  • 1970-01-01
  • 1970-01-01
  • 2018-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多