协慌网

登录 贡献 社区

AngularJS:在调用 $ scope 时防止错误 $ digest 正在进行中。$ apply()

我发现自从以角度构建应用程序后,我需要手动将页面更新到我的范围。

我知道这样做的唯一方法是从我的控制器和指令的范围调用$apply() 。这个问题是它不断向控制台抛出一个错误:

错误:$ digest 已在进行中

有谁知道如何避免这个错误或以不同的方式实现相同的事情?

答案

不要使用这种模式 - 这最终会导致比它解决的更多错误。即使你认为它固定了一些东西,它也没有。

您可以通过检查$scope.$$phase来检查$scope.$$phase $digest是否已在进行中。

if(!$scope.$$phase) {
  //$digest or $apply
}

如果$digest$apply正在进行中, $scope.$$phase将返回"$digest""$apply" 。我相信这些状态之间的区别在于$digest将处理当前范围及其子项的监视, $apply将处理所有范围的观察者。

对于 @ dnc253 来说,如果你发现自己经常叫$digest$apply ,你可能做错了。我通常发现当需要更新范围的状态时,我需要消化,因为 DOM 事件在 Angular 的范围之外触发。例如,当 twitter 引导模式变为隐藏时。有时,当$digest正在进行时,DOM 事件会触发,有时则不会。这就是我使用这张支票的原因。

如果有人知道,我很想知道一个更好的方法。


来自评论:@anddoutoi

angular.js 反模式

  1. 不要做if (!$scope.$$phase) $scope.$apply() ,这意味着你的$scope.$apply()在调用堆栈中不够高。

最近与 Angular 人就这个话题进行了讨论: 出于面向未来的原因,你不应该使用$$phase

当按下 “正确” 方式时,答案是当前的

$timeout(function() {
  // anything you want can go here and will safely be run on the next digest.
})

我最近在编写角度服务时遇到了这个问题,以包装 facebook,google 和 twitter API,这些 API 在不同程度上都有回调。

这是服务中的一个例子。 (为了简洁起见,服务的其余部分 - 设置变量,注入 $ timeout 等 - 已被取消。)

window.gapi.client.load('oauth2', 'v2', function() {
    var request = window.gapi.client.oauth2.userinfo.get();
    request.execute(function(response) {
        // This happens outside of angular land, so wrap it in a timeout 
        // with an implied apply and blammo, we're in action.
        $timeout(function() {
            if(typeof(response['error']) !== 'undefined'){
                // If the google api sent us an error, reject the promise.
                deferred.reject(response);
            }else{
                // Resolve the promise with the whole response if ok.
                deferred.resolve(response);
            }
        });
    });
});

请注意,$ timeout 的 delay 参数是可选的,如果未设置则默认为 0( $ timeout调用$ browser.defer如果未设置延迟,则默认为 0

有点不直观,但这是写 Angular 的人的答案,所以这对我来说已经足够了!

摘要周期是同步调用。在完成之前,它不会控制浏览器的事件循环。有几种方法可以解决这个问题。解决这个问题最简单的方法是使用内置的 $ timeout,第二种方法是使用下划线或 lodash(你应该这样),请调用以下内容:

$timeout(function(){
    //any code in here will automatically have an apply run afterwards
});

或者如果你有下划线:

_.defer(function(){$scope.$apply();});

我们尝试了几种解决方法,我们讨厌将 $ rootScope 注入我们的所有控制器,指令甚至一些工厂。所以,到目前为止,$ timeout 和_.defer 一直是我们的最爱。这些方法成功地告诉 angular 等待下一个动画循环,这将保证当前范围。$ apply 结束。