$ scope和$$阶段的解决方法是否始终在AngularJS中按预期工作?

时间:2012-11-30 05:41:49

标签: angularjs

在AngularJS中处理第三方工具以及外部'DOM事件时要记住的一件重要事情是使用$scope.$apply()方法操作来启动更改。这很有效,但有时范围本身我已经在通过摘要(基本上是$ apply方法触发)和调用$ apply进行研究将会引发错误。因此,为了解决这个问题,您必须注意每当摘要进行时设置为范围的$scope.$$phase标志。

现在,让我们假设您要更改网址并启动:

$scope.$apply(function() {
  $location.path('/home');
});

这可以按预期工作,但现在让我们假设$ scope正忙于做这件事。因此,您需要检查$$阶段变量并假设您的更改将被选中:

if($scope.$$phase) {
  $location.path('/home');
}
else {
  $scope.$apply(function() {
    $location.path('/home');
  });
}

这就是我一直在做的事情(显然不是代码重复),它似乎100%的时间都在工作。我关心的是当范围处于消化中途时,AngularJS如何获取变化?

也许这个例子不够具体。让我们假设一些更大的东西。想象一下,如果你有一个庞大的网页,其中包含大量的绑定,并假设消化将线性地咀嚼页面(我假设它在优先级方面做了类似的事情......在这种情况下,无论是在首先是DOM树)并从上到下更新页面上的绑定。

<div class="binding">{{ binding1 }}</div>
<div class="binding">{{ binding2 }}</div>
<div class="binding">{{ binding3 }}</div>
<div class="binding">{{ binding4 }}</div>
<div class="binding">{{ binding5 }}</div>
<div class="binding">{{ binding6 }}</div>
<div class="binding">{{ binding7 }}</div>
<div class="binding">{{ binding8 }}</div>

让我们假设消化正在进行,并且它位于消化队列中间的某个位置。现在让我们尝试在某个地方更改页面顶部的绑定值

if($scope.$$phase) {
  $scope.binding1 = 'henry';
}

现在,不知何故,AngularJS会接收更改并正确更新绑定。即使可以认为更改本身在队列中的早期相对于HTML / DOM也是如此。

我的问题是AngularJS如何管理这种潜在的竞争条件?如果 binding8 更新(因为它位于页面下方),我可以稍微舒服一点,但因为 binding1 也会更新(立即无需再次调用$ apply),让我有点失落。这是否意味着在两者之间的某处发送了另一种消化?或者$ scope对象是否比我预期的更神奇?我会假设这个问题已经存在过,但是因为首先发现关于$$阶段和$ scope的问题有点棘手,所以我假设这也可能是一个落后的问题。

有什么想法吗?

3 个答案:

答案 0 :(得分:19)

关于绑定和竞争条件。 $ digest 将循环所有观察者,直到没有任何变化。正如您可以通过向观察者/绑定方法添加日志来观察它,它将至少调用每个绑定/观察者两次以确保没有更改并且所有绑定值都是稳定的。这些只是脏检查,直到每个值都被解析(在2次循环迭代中没有改变)。希望有所帮助。

这在AngularJS文档中有解释:http://docs.angularjs.org/api/ng.$rootScope.Scope#$digest

注意:这是matsko要求复制/粘贴我的评论。

答案 1 :(得分:1)

Apply将继续致电digest,直到确定没有任何变化为止。因此,如果在第一次通话时可能有竞争条件,但第二次通话将总是有所补偿。

答案 2 :(得分:0)

你不应该使用这种解决方法:如果$scope的状态不确定这个函数,你应该在你的调用堆栈中将它消化得更高,你知道你是否通过角度同步处理,因此无需消化,或者您正在异步修改模型,以便消化它。

在旁注中,$scope.$apply基本上是一个带有错误处理的$scope.$root.$digest,因此如果您要更新指令中的隔离范围,则可以通过调用$digest来代替$apply

// Somewhere in a callback
$location.path('/home');
$scope.$digest();

PS:这是为了纠正电子满意答案:$digest也会在范围变脏时调用自己。