如何使用$scope。$watch和$scope。$apply在AngularJS?

我不懂如何使用$scope.$watch$scope.$apply。官方文件没有帮助。

我不太明白的是:

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

我试过第二条,但它认为理解第一条和第一条是理所当然的。

$apply$watch做什么,我如何恰当地使用它们?

1004789 次浏览

为了理解AngularJS,你需要了解它是如何工作的。

摘要周期和$范围

首先,AngularJS定义了一个所谓的消化周期概念。这个循环可以被认为是一个循环,在此期间AngularJS检查所有变量看着是否有任何变化。因此,如果你在控制器中定义了$scope.myVar,而这个变量是因为被监视而被标记,那么你就隐式地告诉AngularJS在循环的每次迭代中监视myVar的变化。

一个自然的后续问题是:是否所有与$scope相关的内容都被监视?幸运的是,没有。如果您要观察$scope中每个对象的更改,那么快速的摘要循环将花费很长时间来评估,并且很快就会遇到性能问题。这就是为什么AngularJS团队给了我们两种方法来声明一些$scope变量被监视(阅读下文)。

$watch帮助监听$scope的变化

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

  1. 通过表达式<span>\{\{myVar}}</span>在模板中使用它
  2. 通过$watch服务手动添加它
< p >广告1)这是最常见的场景,我相信你以前见过,但你不知道这在背景中创建了一个手表。是的,它有!使用AngularJS指令(比如ng-repeat)也可以创建隐式手表< p >广告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允许将更改与摘要周期集成

你可以考虑第二条。你看,每次你直接改变观察与1号相关的变量对象时,AngularJS就会知道改变已经发生了。这是因为AngularJS已经知道如何监控这些变化。因此,如果它发生在框架管理的代码中,则将继续进行摘要循环。

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

为了解决这个问题,引入了$apply。它让你明确地开始消化周期。但是,你应该只使用这个方法将一些数据迁移到AngularJS(与其他框架集成),而不要将这个方法与常规的AngularJS代码结合使用,因为AngularJS会抛出一个错误。

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

好吧,既然你知道了所有这些,你真的应该再跟着教程走一遍。摘要周期将确保UI和JavaScript代码保持同步,只要没有任何变化,就会计算附加到所有$scope的每个观察者。如果在摘要循环中没有发生更多的更改,则认为它已完成。

你可以在控制器中显式地将对象附加到$scope对象上,或者直接在视图中以\{\{expression}}的形式声明它们。

进一步阅读:

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

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

概念页(“运行时”部分)很好地解释了$digest循环、$apply、$evalAsync队列和$watch列表。下面是文字配图:

$digest loop

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

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

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

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

这里是文档链接

AngularJS扩展了事件循环,创建了AngularJS context

看()美元

每次你在UI中绑定东西时,你都会插入#0列表中的#0

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

这里我们有$scope.user,它绑定到第一个输入,我们有$scope.pass,它绑定到第二个输入。这样我们就加了两个#2es到#2列表

当我们的模板被加载时,也就是在链接阶段,编译器会寻找每一个指令并创建所有需要的$watches。

AngularJS提供了$watch$watchcollection$watch(true)。下面是一个简洁的图表,解释了从第3条中选取的所有三个。

Enter image description here

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/

# 0循环

当浏览器接收到一个可以由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的变化报告一个变化,它将强制另一个$digest循环
  4. 新的循环没有报告任何内容。
  5. 浏览器取回控件并更新DOM反映$scope.name的新值
  6. 这里很重要的一点是,每个进入AngularJS上下文的事件都会运行$digest循环。这意味着每当我们在输入中写入一个字母时,循环将检查该页中的每个$watch

$应用()

如果你在事件被触发时调用$apply,它将通过angular上下文,但如果你不调用它,它将在它外部运行。就这么简单。#0调用#2在内部循环,它将遍历所有的手表,以确保DOM使用新更新的值进行更新。

$apply()方法将触发整个$scope链上的观察者,而$digest()方法只会触发当前$scopechildren链上的观察者。# 7

我发现了非常深入的视频,涵盖了$watch$apply$digest和消化周期:

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

Enter image description here

在上面的图像中,“$scope.c”没有被监视,因为它没有用于任何数据绑定(在标记中)。另外两个($scope.a$scope.b)将受到关注。

Enter image description here

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

关于$apply$digest:

Enter image description here

读完上面所有的,无聊又困(抱歉,但这是真的)。非常专业,深入,详细,枯燥。我为什么要写作?因为AngularJS是庞大的,很多相互关联的概念可以让任何人发疯。我经常问自己,难道我不够聪明,不能理解它们吗?不!这是因为很少有人能用所有的术语来解释这项技术!好的,让我试试:

1)它们都是事件驱动的事物。(我听到了笑声,但继续读)

如果你不知道什么是事件驱动假设你放了一个按钮在页面上,使用“On -click”将其与函数连接起来,等待用户点击它来触发你在植物里面的动作函数。或者想想“触发器”;

. SQL Server / Oracle

2) $watch是“on-click”。

它的特殊之处在于它以两个函数作为参数,第一个给出事件的值,第二个函数取事件的值考虑…< / p >< p > # 0,但是一个好老板

4)当你想手动操作时,$apply为你提供了方法,就像一个防故障(以防on-click不起作用,你可以强制它运行)。

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

在餐厅里,

——服务员

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

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

第一条:到处走动,确保所有服务员都醒着,对顾客的任何变化做出反应。这是$digest()

——老板有最终的力量驱使每个人的要求,这是$apply()