官网上有对这个问题的详细解释和解决方法: https://docs.angularjs.org/error/$rootScope/inprog?p0=$digest。
下面我简单地讲一下原因和解决方法:
假设我现在定义了一个指令,该指令会监听 attr.setFocusIf的值,当该值发生变化时,元素element获得焦点。
myApp.directive('setFocusIf', function() {
return {
link: function($scope, $element, $attr) {
$scope.$watch($attr.setFocusIf, function(value) {
if ( value ) { $element[0].focus(); }
});
}
};
});
假设我在html中是这样使用这个指令的
<input set-focus-if="hasFocus" ng-focus="msg='has focus'">
<button ng-click="hasFocus = true">Focus</button>
此时,有两种方式触发 ngFocus动作,第一种方式的执行过程如下:
(1)点击input表单
(2)input表单获得焦点
(3)触发ng-focus,启动一个新的 $apply() 将 msg的值设置为 "has fcous"
第二种方式的过程如下:
(1)点击按钮
(2)ng-click被触发,启动一个新的$apply() 将hasFcous的值设置为true
(3)$apply()启动 $digest() ,该$digest触发 setFocusIF指令中的$watch回调函数
(4)$watch回调函数执行,使得input表单获取焦点
(5)ng-focus指令被触发,启动一个新的$apply()将 msg的值设置为 "has fcous" (问题出现了,在一个$digest中启动了另外一个$digest ,因此抛出异常)
解决方法:
知道原因之后,解决方法也很简单,只要第(5)步在一个干净的环境启用新的$digest即可。使用$timeout(fn, 0 , false)。最有一个false告诉angularjs不要将该fn包含在一个$apply()中。
修改 指令的代码如下:
myApp.directive('setFocusIf', function($timeout) {
return {
link: function($scope, $element, $attr) {
$scope.$watch($attr.setFocusIf, function(value) {
if ( value ) {
$timeout(function() {
// We must reevaluate the value in case it was changed by a subsequent
// watch handler in the digest.
if ( $scope.$eval($attr.setFocusIf) ) {
$element[0].focus();
}
}, 0, false);
}
});
}
}
});
此时运行即无异常