我发现自从在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的人的答案,所以这对我来说足够好了!

其他回答

当我们要求angular运行摘要循环时,问题基本上就要出现了,尽管它正在进行中,这对angular的理解造成了问题。控制台中的结果异常。1.调用范围没有任何意义$$timeout函数中的apply(),因为在内部它也会这样做。2.该代码使用普通JavaScript函数,因为它的本机非角度定义,即setTimeout3.要做到这一点,你可以利用if(!scope.$$phase){范围$evalAsync(函数){});}

如果你这样做,有时你仍然会出错(https://stackoverflow.com/a/12859093/801426).

试试看:

if(! $rootScope.$root.$$phase) {
...

我一直在使用这种方法,它似乎工作得很好。这只是等待循环完成的时间,然后触发apply()。只需从任意位置调用函数apply(<yourscope>)。

function apply(scope) {
  if (!scope.$$phase && !scope.$root.$$phase) {
    scope.$apply();
    console.log("Scope Apply Done !!");
  } 
  else {
    console.log("Scheduling Apply after 200ms digest cycle already in progress");
    setTimeout(function() {
        apply(scope)
    }, 200);
  }
}

找到此:https://coderwall.com/p/ngisma其中Nathan Walker(靠近页面底部)建议在$rootScope中使用decorator来创建func“safeApply”,代码:

yourAwesomeModule.config([
  '$provide', function($provide) {
    return $provide.decorator('$rootScope', [
      '$delegate', function($delegate) {
        $delegate.safeApply = function(fn) {
          var phase = $delegate.$$phase;
          if (phase === "$apply" || phase === "$digest") {
            if (fn && typeof fn === 'function') {
              fn();
            }
          } else {
            $delegate.$apply(fn);
          }
        };
        return $delegate;
      }
    ]);
  }
]);

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

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

或者如果你有lodash:

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

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