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

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

错误:$digest已在进行中

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


当前回答

从最近与Angular团队就这个主题进行的讨论中可以看出:出于防将来的原因,您不应该使用$$phase

当被问及“正确”的做法时,答案是当前

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

我最近在编写angular服务来包装facebook、谷歌和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的延迟参数是可选的,如果未设置,则默认值为0($timeout调用$browser.deffer,如果没有设置延迟,则默认为0)

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

其他回答

看见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,因为它会做大致相同的事情。

类似于上面的答案,但这对我来说非常有效。。。在服务中添加:

    //sometimes you need to refresh scope, use this to prevent conflict
    this.applyAsNeeded = function (scope) {
        if (!scope.$$phase) {
            scope.$apply();
        }
    };

我建议您使用自定义事件,而不是触发摘要循环。

我发现,广播自定义事件并为此事件注册侦听器是一个很好的解决方案,无论您是否处于摘要周期,都可以触发您希望发生的操作。

通过创建自定义事件,您的代码也会更高效,因为您只触发订阅所述事件的侦听器,而不会像调用作用域时那样触发绑定到作用域的所有监视$申请

$scope.$on('customEventName', function (optionalCustomEventArguments) {
   //TODO: Respond to event
});


$scope.$broadcast('customEventName', optionalCustomEventArguments);

从最近与Angular团队就这个主题进行的讨论中可以看出:出于防将来的原因,您不应该使用$$phase

当被问及“正确”的做法时,答案是当前

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

我最近在编写angular服务来包装facebook、谷歌和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的延迟参数是可选的,如果未设置,则默认值为0($timeout调用$browser.deffer,如果没有设置延迟,则默认为0)

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

尝试使用

$scope.applyAsync(function() {
    // your code
});

而不是

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

$applysync将$apply的调用安排为稍后进行。这可用于对需要在同一摘要中计算的多个表达式进行排队。

注意:在$digest中,如果当前作用域是$rootScope,则$applySync()将仅刷新。这意味着,如果在子作用域上调用$digest,它将不会隐式刷新$applysync()队列。

Exmaple公司:

  $scope.$applyAsync(function () {
                if (!authService.authenticated) {
                    return;
                }

                if (vm.file !== null) {
                    loadService.setState(SignWizardStates.SIGN);
                } else {
                    loadService.setState(SignWizardStates.UPLOAD_FILE);
                }
            });

参考文献:

1.范围.$applySync()与范围$AngularJS 1.3中的evalAsync()

AngularJs文件