1116

我不明白如何使用$scope.$watch$scope.$apply。官方文档没有帮助。

我不明白的具体内容:

  • 它们是否连接到 DOM?
  • 如何更新模型的 DOM 更改?
  • 它们之间的连接点是什么?

我试过这个教程,但它需要理解$watch$apply理所当然。

做什么$apply$watch做什么,以及如何正确使用它们?

4

6 回答 6

1776

您需要了解 AngularJS 的工作原理才能理解它。

摘要循环和 $scope

首先,AngularJS 定义了一个所谓的摘要循环的概念。这个循环可以被认为是一个循环,在这个循环中,AngularJS 会检查所有s观察到的所有变量是否有任何变化$scope。因此,如果您已$scope.myVar在控制器中定义并且此变量被标记为被监视,那么您就隐含地告诉 AngularJS 监视myVar循环的每次迭代中的更改。

一个自然的后续问题是:一切都与$scope被监视有关吗?幸运的是,没有。如果您要注意$scope. 这就是为什么 AngularJS 团队给了我们两种方法来声明某个$scope变量被监视(阅读下文)。

$watch 有助于监听 $scope 的变化

有两种方法可以将$scope变量声明为被监视。

  1. 通过表达式在模板中使用它<span>{{myVar}}</span>
  2. 通过$watch服务手动添加

广告 1) 这是最常见的场景,我相信你以前见过,但你不知道这在后台创建了一个手表。是的,它有!使用 AngularJS 指令(例如ng-repeat)也可以创建隐式监视。

广告 2) 这就是您创建自己的手表的方式。当附加到的某些值发生更改$watch时,服务可以帮助您运行一些代码。$scope它很少使用,但有时很有帮助。例如,如果您想在每次“myVar”更改时运行一些代码,您可以执行以下操作:

function MyController($scope) {

    $scope.myVar = 1;

    $scope.$watch('myVar', function() {
        alert('hey, myVar has changed!');
    });

    $scope.buttonClicked = function() {
        $scope.myVar = 2; // This will trigger $watch expression to kick in
    };
}

$apply 可以将更改与摘要循环集成

您可以将该$apply功能视为一种集成机制。你看,每次你改变一些直接附加到$scope对象的被监视变量时,AngularJS 都会知道改变已经发生。这是因为 AngularJS 已经知道要监视这些更改。因此,如果它发生在框架管理的代码中,摘要循环将继续进行。

但是,有时您想更改 AngularJS 世界之外的某些值并查看更改正常传播。考虑这一点 - 你有一个$scope.myVar将在 jQuery 的$.ajax()处理程序中修改的值。这将在未来的某个时候发生。AngularJS 不能等待这种情况发生,因为它没有被指示等待 jQuery。

为了解决这个问题,$apply已经推出。它使您可以明确地启动消化周期。但是,您应该只使用此方法将一些数据迁移到 AngularJS(与其他框架集成),而不要将此方法与常规 AngularJS 代码结合使用,因为 AngularJS 会抛出错误。

所有这些与 DOM 有什么关系?

好吧,既然您已经了解了这一切,那么您真的应该再次按照本教程进行操作。摘要循环将通过评估附加到所有 s 的每个观察者来确保 UI 和 JavaScript 代码保持同步,$scope只要没有任何变化。如果摘要循环中不再发生更改,则认为它已完成。

您可以在控制器中显式地将对象附加到对象,也可以直接在视图$scope中以表单形式声明它们。{{expression}}

进一步阅读:

于 2013-02-27T13:14:33.173 回答
162

在 AngularJS 中,我们更新我们的模型,我们的视图/模板“自动”更新 DOM(通过内置或自定义指令)。

$apply 和 $watch 都是 Scope 方法,与 DOM 无关。

概念页面( “运行时”部分)对 $digest 循环、$apply、$evalAsync 队列和 $watch 列表有很好的解释。这是文本随附的图片:

$digest 循环

任何可以访问范围的代码——通常是控制器和指令(它们的链接函数和/或它们的控制器)——都可以设置一个“ watchExpression ”,AngularJS 将针对该范围进行评估。每当 AngularJS 进入它的 $digest 循环(特别是“$watch list”循环)时,就会发生这种评估。你可以观察单个作用域属性,你可以定义一个函数来同时观察两个属性,你可以观察一个数组的长度,等等。

当事情发生在“AngularJS 内部”时——例如,你输入一个启用了 AngularJS 双向数据绑定的文本框(即,使用 ng-model),触发一个 $http 回调等等——$apply 已经被调用了,所以我们'在上图中的“AngularJS”矩形内。所有 watchExpressions 都将被评估(可能不止一次——直到没有检测到进一步的变化)。

当事情发生在“AngularJS 之外”时——例如,你在指令中使用了 bind(),然后该事件触发,导致你的回调被调用,或者一些 jQuery 注册的回调被触发——我们仍然在“Native”矩形中。如果回调代码修改了任何 $watch 正在观察的东西,调用 $apply 进入 AngularJS 矩形,导致 $digest 循环运行,因此 AngularJS 会注意到变化并发挥它的魔力。

于 2013-02-28T00:48:42.743 回答
55

AngularJS 扩展了这个事件循环,创建了一个名为AngularJS context.

$手表()

每次在 UI 中绑定某些内容时,都会$watch$watchlist中插入一个。

User: <input type="text" ng-model="user" />
Password: <input type="password" ng-model="pass" />

这里我们有$scope.user,它绑定到第一个输入,我们有$scope.pass,它绑定到第二个输入。这样做我们将两个$watches 添加到$watch列表中

当我们的模板被加载时,也就是链接阶段,编译器将查找每个指令并创建所有$watch需要的 es。

AngularJS 提供$watch,$watchcollection$watch(true). 下面是一个简洁的图表,详细解释了从观察者那里获取的所有三个。

在此处输入图像描述

angular.module('MY_APP', []).controller('MyCtrl', MyCtrl)
function MyCtrl($scope,$timeout) {
  $scope.users = [{"name": "vinoth"},{"name":"yusuf"},{"name":"rajini"}];

  $scope.$watch("users", function() {
    console.log("**** reference checkers $watch ****")
  });

  $scope.$watchCollection("users", function() {
    console.log("**** Collection  checkers $watchCollection ****")
  });

  $scope.$watch("users", function() {
    console.log("**** equality checkers with $watch(true) ****")
  }, true);

  $timeout(function(){
     console.log("Triggers All ")
     $scope.users = [];
     $scope.$digest();

     console.log("Triggers $watchCollection and $watch(true)")
     $scope.users.push({ name: 'Thalaivar'});
     $scope.$digest();

     console.log("Triggers $watch(true)")
     $scope.users[0].name = 'Superstar';
     $scope.$digest();
  });
}

http://jsfiddle.net/2Lyn0Lkb/

$digest环形

当浏览器接收到可以由 AngularJS 上下文管理的事件时,$digest将触发循环。这个循环由两个较小的循环组成。一个处理$evalAsync队列,另一个处理$watch list. 将$digest遍历$watch我们拥有的列表

app.controller('MainCtrl', function() {
  $scope.name = "vinoth";

  $scope.changeFoo = function() {
      $scope.name = "Thalaivar";
  }
});

{{ name }}
<button ng-click="changeFoo()">Change the name</button>

这里我们只有一个$watch,因为 ng-click 不会创建任何手表。

我们按下按钮。

  1. 浏览器接收到一个将进入 AngularJS 上下文的事件
  2. 循环将$digest运行并询问每个 $watch 是否有更改。
  3. 由于$watch正在监视 $scope.name 更改的 which 报告了更改,因此它将强制执行另一个$digest循环。
  4. 新循环不报告任何内容。
  5. 浏览器取回控制权并更新 DOM,反映 $scope.name 的新值
  6. 这里重要的是每个进入 AngularJS 上下文的事件都会运行一个$digest循环。这意味着每次我们在输入中写一个字母时,循环都会运行检查$watch此页面中的每个字母。

$申请()

如果您$apply在触发事件时调用,它将通过角度上下文,但如果您不调用它,它将在它之外运行。就是这么简单。$apply将在内部调用$digest()循环,并遍历所有手表以确保 DOM 使用新更新的值进行更新。

$apply()方法将触发整个$scope链上的观察者,而该$digest()方法只会触发当前$scope及其children. 当没有更高级别的$scope对象需要知道本地更改时,您可以使用$digest().

于 2016-04-22T13:06:59.507 回答
18

$watch我在以下位置找到了涵盖、$apply$digest摘要 周期的非常深入的视频:

以下是这些视频中用于解释概念的几张幻灯片(以防万一,如果上述链接被删除/不起作用)。

在此处输入图像描述

在上图中,“$scope.c”没有被监视,因为它没有用于任何数据绑定(在标记中)。其他两个 ($scope.a$scope.b) 将被观看。

在此处输入图像描述

从上图中:AngularJS 根据各自的浏览器事件捕获事件,执行摘要循环(遍历所有监视以进行更改),执行监视函数并更新 DOM。如果不是浏览器事件,可以使用$apply或手动触发摘要循环$digest

更多关于$apply$digest

在此处输入图像描述

于 2016-11-20T16:28:37.220 回答
17

$watchGroup$watchCollection也有。具体来说,$watchGroup如果你想调用一个函数来更新一个在不是 dom 对象的视图中具有多个属性的对象,例如画布、WebGL或服务器请求中的另一个视图,这真的很有帮助。

在这里,文档链接

于 2015-03-18T10:50:18.770 回答
15

读完以上所有内容,无聊而困倦(对不起,但这是真的)。非常技术性,深入,详细和干燥。我为什么要写作?因为 AngularJS 非常庞大,许多相互关联的概念会让任何人发疯。我经常问自己,我不够聪明,无法理解他们吗?不!这是因为很少有人能用一种没有所有术语的傻瓜语言来解释这项技术!好吧,让我试试:

1)它们都是事件驱动的东西。(我听到笑声,但请继续阅读)

如果您不知道什么是事件驱动然后认为您在页面上放置了一个按钮,使用“点击”将其与一个功能挂钩,等待用户点击它以触发您在页面中植入的操作功能。或者想想 SQL Server / Oracle 的“触发器”。

2) $watch 是“点击”。

有什么特别之处在于它需要 2 个函数作为参数,第一个给出事件的值,第二个考虑值...

3) $digest 是一个不知疲倦地到处检查的老板,bla-bla-bla 但是一个好老板。

4) $apply 为您提供手动操作的方式,例如防故障(如果单击不启动,则强制它运行。)

现在,让我们让它可视化。想象一下,这样更容易抓住这个想法:

在一家餐馆,

- 服务员

应该接受客户的订单,这是

$watch(
  function(){return orders;},
  function(){Kitchen make it;}
);

- 经理跑来跑去确保所有服务员都醒着,对客户的任何变化迹象做出反应。这是$digest()

- OWNER拥有根据要求驾驶每个人的终极权力,这是$apply()

于 2017-02-07T01:20:51.990 回答