Directive binding to functions end up in $digest loop -- Error: $rootScope:infdig
Directive binding to functions end up in $digest loop -- Error: $rootScope:infdig
我想要一个与对象列表一起工作的指令,但我还需要接受双向绑定或函数绑定。
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
属性与 returns 列表的函数一起使用时,我收到此错误:
VM607 angular.js:68 Uncaught Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: [[{"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: regularInterceptedExpression","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
at VM607 angular.js:68
at Scope.$digest (VM607 angular.js:17893)
at Scope.$apply (VM607 angular.js:18125)
at done (VM607 angular.js:12233)
at completeRequest (VM607 angular.js:12459)
at XMLHttpRequest.requestLoaded (VM607 angular.js:12387)
我创建了 this plunker 示例(打开 javascript 控制台,您可以看到这些错误)演示了我的需要。在这个例子中,我得到了这些错误,但视图仍然得到更新。在我正在开发的应用程序(更大)中,我遇到了很多 $digest
错误,导致网站变慢并且视图没有更新。
在不结束无限循环的情况下绑定函数的最佳方法是什么?
Angular 在 get_list()
表达式上注册一个隐式的 $watchCollection。此表达式将至少调用两次,因为 angular 想要检查模型是否已稳定。
<ul>
<li ng-repeat="obj in get_list()">
name: {{ obj.name }}<br />
age: {{ obj.age }}
</li>
</ul>
您的代码 returns 每次调用 get_list()
时都会有不同的对象数组,因此 angular 会继续消化,直到达到最大消化周期限制。
$scope.get_list = function(val) {
return function() {
return [ { name: "val", age: val } ];
}
}
即使内容相同,函数每次调用都会创建新对象。
避免在模板中使用函数调用。而是将值放在范围内,并让指令对这些值的变化做出反应。
来自文档:
Error: $rootScope:infdig
This error occurs when the application's model becomes unstable and each $digest
cycle triggers a state change and subsequent $digest
cycle. AngularJS detects this situation and prevents an infinite loop from causing the browser to become unresponsive.
One common mistake is binding to a function which generates a new array every time it is called. For example:
<div ng-repeat="user in getUsers()">{{ user.name }}</div>
$scope.getUsers = function() {
return [ { name: 'Hank' }, { name: 'Francisco' } ];
};
Since getUsers()
returns a new array, AngularJS determines that the model is different on each $digest
cycle, resulting in the error. The solution is to return the same array object if the elements have not changed:
var users = [ { name: 'Hank' }, { name: 'Francisco' } ];
$scope.getUsers = function() {
return users;
};
Should I give up with this kind of functions and use a filter instead?
TL;DR 是的。
组件应该只是作为 "skins"。他们应该只显示提供给他们的数据并将用户事件传达给父控制器。模型应该是 Single Source of Truth。指令不需要从父控制器获取数据。
表达式&
绑定应该只用于将用户事件传递给父控制器。然后父控制器应该适当地更改模型。 Separation of Concerns 使应用程序更易于理解、调试、测试和维护。
One more question: this also applies for functions returning functions, right?
应该避免"functions returning functions"模式。它构成了一个令人困惑的模板。而是使用局部变量调用函数:
<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
来符合Angular2+的约定。
来自文档:
&
or &attr
- provides a way to execute an expression in the context of the parent scope. If no attr name is specified then the attribute name is assumed to be the same as the local name. Given <my-component my-attr="count = count + $value">
and the isolate scope definition scope: { localFn:'&myAttr' }
, the isolate scope property localFn
will point to a function wrapper for the count = count + $value
expression. Often it's desirable to pass data from the isolated scope via an expression to the parent scope. This can be done by passing a map of local variable names and values into the expression wrapper fn. For example, if the expression is increment($amount)
then we can specify the amount value by calling the localFn
as localFn({$amount: 22})
.
我想要一个与对象列表一起工作的指令,但我还需要接受双向绑定或函数绑定。
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
属性与 returns 列表的函数一起使用时,我收到此错误:
VM607 angular.js:68 Uncaught Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting! Watchers fired in the last 5 iterations: [[{"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: regularInterceptedExpression","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 at VM607 angular.js:68 at Scope.$digest (VM607 angular.js:17893) at Scope.$apply (VM607 angular.js:18125) at done (VM607 angular.js:12233) at completeRequest (VM607 angular.js:12459) at XMLHttpRequest.requestLoaded (VM607 angular.js:12387)
我创建了 this plunker 示例(打开 javascript 控制台,您可以看到这些错误)演示了我的需要。在这个例子中,我得到了这些错误,但视图仍然得到更新。在我正在开发的应用程序(更大)中,我遇到了很多 $digest
错误,导致网站变慢并且视图没有更新。
在不结束无限循环的情况下绑定函数的最佳方法是什么?
Angular 在 get_list()
表达式上注册一个隐式的 $watchCollection。此表达式将至少调用两次,因为 angular 想要检查模型是否已稳定。
<ul>
<li ng-repeat="obj in get_list()">
name: {{ obj.name }}<br />
age: {{ obj.age }}
</li>
</ul>
您的代码 returns 每次调用 get_list()
时都会有不同的对象数组,因此 angular 会继续消化,直到达到最大消化周期限制。
$scope.get_list = function(val) {
return function() {
return [ { name: "val", age: val } ];
}
}
即使内容相同,函数每次调用都会创建新对象。
避免在模板中使用函数调用。而是将值放在范围内,并让指令对这些值的变化做出反应。
来自文档:
Error: $rootScope:infdig
This error occurs when the application's model becomes unstable and each
$digest
cycle triggers a state change and subsequent$digest
cycle. AngularJS detects this situation and prevents an infinite loop from causing the browser to become unresponsive.One common mistake is binding to a function which generates a new array every time it is called. For example:
<div ng-repeat="user in getUsers()">{{ user.name }}</div>
$scope.getUsers = function() { return [ { name: 'Hank' }, { name: 'Francisco' } ]; };
Since
getUsers()
returns a new array, AngularJS determines that the model is different on each$digest
cycle, resulting in the error. The solution is to return the same array object if the elements have not changed:var users = [ { name: 'Hank' }, { name: 'Francisco' } ]; $scope.getUsers = function() { return users; };
Should I give up with this kind of functions and use a filter instead?
TL;DR 是的。
组件应该只是作为 "skins"。他们应该只显示提供给他们的数据并将用户事件传达给父控制器。模型应该是 Single Source of Truth。指令不需要从父控制器获取数据。
表达式&
绑定应该只用于将用户事件传递给父控制器。然后父控制器应该适当地更改模型。 Separation of Concerns 使应用程序更易于理解、调试、测试和维护。
One more question: this also applies for functions returning functions, right?
应该避免"functions returning functions"模式。它构成了一个令人困惑的模板。而是使用局部变量调用函数:
<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
来符合Angular2+的约定。
来自文档:
&
or&attr
- provides a way to execute an expression in the context of the parent scope. If no attr name is specified then the attribute name is assumed to be the same as the local name. Given<my-component my-attr="count = count + $value">
and the isolate scope definitionscope: { localFn:'&myAttr' }
, the isolate scope propertylocalFn
will point to a function wrapper for thecount = count + $value
expression. Often it's desirable to pass data from the isolated scope via an expression to the parent scope. This can be done by passing a map of local variable names and values into the expression wrapper fn. For example, if the expression isincrement($amount)
then we can specify the amount value by calling thelocalFn
aslocalFn({$amount: 22})
.