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

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

错误:$digest已在进行中

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


当前回答

找到此: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;
      }
    ]);
  }
]);

其他回答

从最近与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的人的答案,所以这对我来说足够好了!

保持此过程干燥的简便小助手方法:

function safeApply(scope, fn) {
    (scope.$$phase || scope.$root.$$phase) ? fn() : scope.$apply(fn);
}

使用$scope$$阶段||$scope$apply();相反

了解到Angular文档将检查$$阶段称为反模式,我尝试使用$timeout和_.deffer来工作。

超时和延迟方法在dom中创建一个未解析的{{myVar}}内容的闪存,就像FOUT一样。对我来说,这是不可接受的。这让我没有太多教条地告诉我,有些东西是黑客,没有合适的选择。

每次唯一有效的方法是:

if(scope.$$phase!==“$digest”){scope.$digest()}。

我不明白这种方法的危险性,也不明白为什么评论中的人和棱角分明的团队会把它描述为黑客。命令看起来很精确,很容易理解:

“做摘要,除非已经发生了”

在CoffeeScript中,它更漂亮:

范围$digest(),除非作用域$$阶段为“$digest”

这有什么问题?有没有替代方案不会产生FOUT$safeApply看起来不错,但也使用$$阶段检查方法。

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

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

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