AngularJS中的$$阶段是什么?

时间:2013-11-28 10:09:19

标签: angularjs

我发现这段代码片段是有人为bootstrap模态编写的angular指令的一部分。

//Update the visible value when the dialog is closed                                                                                                                                                                                                            
                //through UI actions (Ok, cancel, etc.)                                                                                                                                                                                                                         
                element.bind("hide.bs.modal", function () {                                                                                                                                                                                                                     
                    scope.modalVisible = false;                                                                                                                                                                                                                                 
                    if (!scope.$$phase && !scope.$root.$$phase)                                                                                                                                                                                                                 
                        scope.$apply();                                                                                                                                                                                                                                         
                });  

我明白这部分是针对双向绑定的后半部分,我们绑定到hide.bs.modal事件并在UI更改时更新模态。

我只是想知道为什么在调用apply之前检查范围和rootScope的$$阶段的人?

我们不能直接打电话申请吗?

这里的$$阶段是什么?

我试了很多次,找不到任何好的解释。

修改

我在哪里看到了这个例子: Simple Angular Directive for Bootstrap Modal

5 个答案:

答案 0 :(得分:45)

$$phase是一个标志设置,而角度是$digest周期。

有时(在极少数情况下),您希望在执行$$phase之前检查范围$apply。如果您在$apply

期间尝试$digest,则会出错
  

错误:$申请已在进行中

答案 1 :(得分:35)

Davin完全正确,它是在消化周期中角度设定的旗帜。

但请勿在代码中使用它。

我最近有机会向Misko(有角度的作者)询问关于$$阶段,他说永远不会使用它;它是摘要周期的内部实现,并不是未来的安全。

为确保您的代码在将来继续有效,他建议在$ timeout内包装您想要“安全申请”的内容

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

当您在摘要周期中有回调或其他可能解决的事情时(但并非总是如此),这会出现很多事情

以下是我处理谷歌图书馆时的一个示例代码段:(其余的服务来自此处。)

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.defer defaults to 0 if delay isn't set

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

答案 2 :(得分:6)

在该示例中,元素绑定将从非角度事件执行。在大多数情况下,只需在不检查阶段的情况下调用$apply()即可。

但是,如果查看其余代码,则会有一个名为$scope的{​​{1}}函数。此函数调用非角度代码,这可能导致“hide.bs.modal”事件触发。如果事件通过此路由触发,则调用堆栈位于showModal()内。

因此,这个事件确实属于一个罕见的函数,它将从角度管理代码和非角度代码中调用。在这种情况下检查$digest是必要的,因为您不知道事件是如何产生的。如果将$$phase设置为某个值,则摘要循环将完成,并且不需要调用$$phase

此模式通常称为"safe apply"

答案 3 :(得分:2)

我的理解是在消化或应用范围时使用是很好的。如果它是真实的,则意味着目前正在进行$ digest或$ apply阶段。如果你得到相关的错误,你可以做$ scope。$$阶段|| $ scope.digest();只有$ scope。$$ pahse才会消化。

答案 4 :(得分:0)

您可以使用$scope.$evalAsync()方法,而不是像@betaorbust正确建议的那样,通过$scope.$apply()值检查在外部使用$$phase

  

我最近有机会向Misko(角度作者)询问有关$$$ phase的问题,他说从不使用它; 这是摘要周期的内部实现,因此未来也不安全。

$scope.$evalAsync()的好处是,它可以为您提供真正的所需,并通过摘要周期获取新数据,甚至比$ timeout可以更快:

  

到目前为止,我对deferred- $ digest-invocation的处理方法是将$ scope。$ apply()调用替换为$ timeout()服务(在延迟后隐式调用$ apply())。但是,昨天,我发现了$ scope。$ evalAsync()方法。两者都完成同一件事-将表达式评估推迟到以后的某个时间点。但是,$ scope。$ evalAsync()可能会在JavaScript事件循环的同一时刻执行。

用法非常简单:

$scope.$evalAsync(
    function() {
       // Call some method here OR 
       $scope.excutemyMethod(); 

       // assign some value;
       $scope.someVariable = "asd"

    }
);

$evalAsync()内部的方法被添加到异步队列中,以便可以通过内部触发$apply 在接下来的摘要周期中执行,这正是我们真正想要的。

它还包括$timeout调用,以处理最罕见的罕见情况,以观察异步队列长度并等待异步队列中的任务被执行,以使您的功能尽快进入执行周期。请参阅Ben Nadel blog,它解释了这一事实。