我发现自从在angular中构建应用程序以来,我越来越需要手动将页面更新到我的范围。

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

错误:$digest已在进行中

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


当前回答

摘要周期是一个同步调用。在完成之前,它不会将控制权交给浏览器的事件循环。有几种方法可以解决这个问题。处理此问题的最简单方法是使用内置的$timeout,第二种方法是如果您使用下划线或lodash(您应该使用),请调用以下命令:

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

或者如果你有lodash:

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

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

其他回答

当您收到此错误时,基本上意味着它已经在更新视图的过程中。您真的不需要在控制器中调用$apply()。如果您的视图没有按预期进行更新,并且在调用$apply()后出现此错误,那么很可能意味着您没有正确更新模型。如果你发布一些细节,我们可以找出核心问题。

不要使用这种模式-这将导致比它解决的错误更多的错误。即使你认为它修复了一些东西,但它没有。

您可以通过检查$scope来检查$digest是否已在进行中$$阶段

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

$范围$$如果正在进行$digest或$apply,则阶段将返回“$digest”或“$apply”。我认为这些状态之间的区别在于,$digest将处理当前范围及其子范围的监视,而$apply将处理所有范围的监视。

就@dnc253而言,如果你发现自己经常调用$digest或$apply,那么你可能做得不对。我通常发现,当我需要更新作用域的状态时,我需要进行消化,因为DOM事件超出Angular的范围。例如,当twitter引导模式变为隐藏时。有时,当$digest正在进行时,DOM事件会触发,有时则不会。这就是我用这张支票的原因。

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


来自评论:作者@anddoutoi

angular.js反模式

不执行if(!$scope.$$phase)$scope$apply(),表示$scope$apply()在调用堆栈中不够高。

看见http://docs.angularjs.org/error/$rootScope:inprog

当对$apply的调用有时在Angular代码外部异步运行(当应使用$apply时),有时在Angle代码内部同步运行(这导致$digest已经在进行中)时,就会出现问题。

例如,当您拥有一个从服务器异步获取项目并缓存它们的库时,可能会发生这种情况。第一次请求项时,将异步检索该项,以免阻止代码执行。然而,第二次,该项已在缓存中,因此可以同步检索。

防止此错误的方法是确保调用$apply的代码异步运行。这可以通过在调用$timeout时运行代码来完成,延迟设置为0(这是默认值)。然而,在$timeout内调用代码就不需要调用$apply,因为$timeout将自行触发另一个$digest循环,从而执行所有必要的更新等。

解决方案

简而言之,不要这样做:

... your controller code...

$http.get('some/url', function(data){
    $scope.$apply(function(){
        $scope.mydate = data.mydata;
    });
});

... more of your controller code...

执行以下操作:

... your controller code...

$http.get('some/url', function(data){
    $timeout(function(){
        $scope.mydate = data.mydata;
    });
});

... more of your controller code...

只有当您知道运行$apply的代码将始终在Angular代码之外运行时,才调用$apply(例如,您对$apply进行的调用将发生在由Angular代码以外的代码调用的回调内)。

除非有人意识到使用$timeout而不是$apply的一些不利影响,否则我不明白为什么不能总是使用$timeout(零延迟)而不是$app,因为它会做大致相同的事情。

您应该根据上下文使用$evalAsync或$timeout。

这是一个有很好解释的链接:

http://www.bennadel.com/blog/2605-scope-evalasync-vs-timeout-in-angularjs.htm

这将解决您的问题:

if(!$scope.$$phase) {
  //TODO
}