我不明白如何使用$scope.$watch
和$scope.$apply
。官方文档没有帮助。
具体我不明白:
我尝试了this tutorial,但理解$watch
和$apply
是理所当然的。
$apply
和$watch
做了什么,以及如何正确使用它们?
答案 0 :(得分:1709)
您需要了解AngularJS的工作原理才能理解它。
首先,AngularJS定义了一个所谓的摘要周期的概念。这个循环可以被认为是一个循环,在此期间AngularJS检查所有$scope
的所有变量观察是否有任何变化。因此,如果您在控制器中定义了$scope.myVar
并且此变量被标记为正在观看,那么您隐含地告诉AngularJS在每次迭代中监视myVar
上的更改。循环。
一个自然的后续问题是:是否有$scope
被关注的所有内容?幸运的是,没有。如果您要查看$scope
中每个对象的更改,那么很快就会需要一段时间来评估摘要循环,您很快就会遇到性能问题。这就是为什么AngularJS团队给了我们两种方式来声明一些$scope
变量被观察(见下文)。
有两种方法可以将$scope
变量声明为正在观看。
<span>{{myVar}}</span>
$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
函数视为集成机制。你看,每当你直接更改附加到$scope
对象的观察变量时,AngularJS就会知道发生了变化。这是因为AngularJS已经知道要监控这些变化。因此,如果它发生在框架管理的代码中,摘要周期将继续。
但是,有时您希望更改AngularJS世界之外的某些值,并看到更改正常传播。
考虑一下 - 你有一个$scope.myVar
值,它将在jQuery的$.ajax()
处理程序中修改。这将在未来的某个时刻发生。 AngularJS不能等待这种情况发生,因为它没有被指示等待jQuery。
为了解决这个问题,我们引入了$apply
。它可以让您明确地开始消化循环。但是,您应该只使用它来将一些数据迁移到AngularJS(与其他框架集成),但是从不将此方法与常规AngularJS代码结合使用,因为AngularJS会抛出错误。
好吧,你应该再次按照教程,现在你知道了这一切。摘要周期将确保UI和JavaScript代码保持同步,通过评估附加到所有$scope
的每个观察者,只要没有任何变化。如果在摘要循环中没有发生更多变化,则认为它已完成。
您可以在Controller中明确地将对象附加到$scope
对象,也可以直接在视图中以{{expression}}
形式声明它们。
我希望这有助于澄清有关这一切的一些基本知识。
进一步阅读:
答案 1 :(得分:157)
在AngularJS中,我们更新模型,我们的视图/模板“自动”更新DOM(通过内置或自定义指令)。
$ apply和$ watch,都是Scope方法,与DOM无关。
Concepts页面(“运行时”部分)对$ digest循环,$ apply,$ evalAsync队列和$ watch列表有很好的解释。以下是文字附带的图片:
无论代码是否有权访问范围 - 通常是控制器和指令(它们的链接函数和/或它们的控制器) - 都可以设置一个“watchExpression”,AngularJS将根据该范围进行评估。只要AngularJS进入其$ digest循环(特别是“$ watch list”循环),就会发生此评估。您可以观看单个范围属性,您可以定义一个函数来一起观看两个属性,您可以观察数组的长度等。
当事情发生在“AngularJS内部”时 - 例如,您键入一个启用了AngularJS双向数据绑定的文本框(即使用ng-model),$ http回调触发等等 - $ apply已被调用,所以我们在上图中的“AngularJS”矩形内。将评估所有watchExpressions(可能不止一次 - 直到没有检测到进一步的更改)。
当事情发生在“AngularJS之外”时 - 例如,你在一个指令中使用了bind()然后该事件触发,导致你的回调被调用,或者一些jQuery注册的回调触发 - 我们仍然在“Native”中长方形。如果回调代码修改了任何$ watch正在观看的内容,请调用$ apply进入AngularJS矩形,导致$ digest循环运行,因此AngularJS会注意到更改并发挥其魔力。
答案 2 :(得分:61)
This blog已经涵盖所有创建示例和可理解的解释。
AngularJS $scope
函数$watch(), $digest()
和$apply()
是AngularJS中的一些核心函数。了解AngularJS必须了解$watch()
,$digest()
和$apply()
。
当您从视图中的某个位置创建数据绑定到$ scope对象上的变量时,AngularJS会创建一个&#34; watch&#34;内部。手表意味着AngularJS会监视$scope object
上变量的变化。框架是&#34;观看&#34;变量。手表是使用$scope.$watch()
功能创建的,我将在本文后面介绍。
在应用程序的关键点,AngularJS调用$scope.$digest()
函数。此函数遍历所有监视并检查是否有任何监视变量已更改。如果监视变量已更改,则调用相应的侦听器函数。监听器函数执行它需要做的任何工作,例如更改HTML文本以反映监视变量的新值。因此,$digest()
函数触发数据绑定更新。
大多数时候,AngularJS会为您调用$ scope。$ watch()和$scope.$digest()
函数,但在某些情况下,您可能需要自己调用它们。因此,了解它们是如何工作真的很好。
$scope.$apply()
函数用于执行某些代码,然后在此之后调用$scope.$digest()
,以便检查所有监视并调用相应的监听侦听器函数。将AngularJS与其他代码集成时,$apply()
函数非常有用。
我将在本文的其余部分详细介绍$watch(), $digest()
和$apply()
函数。
$scope.watch()
函数创建一个变量的监视。注册手表时,您将两个函数作为参数传递给$watch()
函数:
以下是一个例子:
$scope.$watch(function() {},
function() {}
);
第一个函数是值函数,第二个函数是监听器函数。
值函数应返回正在监视的值。然后,AngularJS可以根据watch函数上次返回的值检查返回的值。这样AngularJS可以确定值是否已更改。这是一个例子:
$scope.$watch(function(scope) { return scope.data.myVar },
function() {}
);
此示例valule函数返回$scope
变量scope.data.myVar
。如果此变量的值发生更改,则将返回不同的值,AngularJS将调用侦听器函数。
注意value函数如何将范围作为参数(名称中没有$)。通过此参数,value函数可以访问$scope
及其变量。如果您需要,值函数也可以查看全局变量,但通常您会看到$scope
变量。
如果值已更改,则侦听器函数应执行其需要执行的操作。也许您需要更改另一个变量的内容,或者设置HTML元素的内容或其他内容。这是一个例子:
$scope.$watch(function(scope) { return scope.data.myVar },
function(newValue, oldValue) {
document.getElementById("").innerHTML =
"" + newValue + "";
}
);
此示例将HTML元素的内部HTML设置为变量的新值,嵌入在b元素中,使值变为粗体。当然,您可以使用代码{{ data.myVar }
完成此操作,但这只是您在侦听器函数中可以执行的操作的示例。
$scope.$digest()
函数遍历$scope object
中的所有监视及其子$ scope对象(如果有的话)。当$digest()
遍历手表时,它会调用每个手表的值函数。如果value函数返回的值与上次调用它返回的值不同,则调用该监视的监听器函数。
只要AngularJS认为有必要,就会调用$digest()
函数。例如,在执行了按钮单击处理程序之后,或者在AJAX
调用返回之后(在执行了done()/ fail()回调函数之后)。
您可能会遇到AngularJS没有为您调用$digest()
函数的一些极端情况。您通常会通过注意数据绑定不更新显示的值来检测到这一点。在这种情况下,请致电$scope.$digest()
,它应该有效。或者,您可以使用$scope.$apply()
代替我将在下一节中解释。
$scope.$apply()
函数将一个函数作为执行的参数,然后在内部调用$scope.$digest()
。这使您更容易确保检查所有手表,从而刷新所有数据绑定。这是一个$apply()
示例:
$scope.$apply(function() {
$scope.data.myVar = "Another value";
});
作为参数传递给$apply()
函数的函数将更改$scope.data.myVar
的值。当函数退出AngularJS时,将调用$scope.$digest()
函数,以便检查所有监视的观察值的变化。
要说明$watch()
,$digest(
)和$apply()
的工作原理,请查看此示例:
<div ng-controller="myController">
{{data.time}}
<br/>
<button ng-click="updateTime()">update time - ng-click</button>
<button id="updateTimeButton" >update time</button>
</div>
<script>
var module = angular.module("myapp", []);
var myController1 = module.controller("myController", function($scope) {
$scope.data = { time : new Date() };
$scope.updateTime = function() {
$scope.data.time = new Date();
}
document.getElementById("updateTimeButton")
.addEventListener('click', function() {
console.log("update time clicked");
$scope.data.time = new Date();
});
});
</script>
他的示例将$scope.data.time
变量绑定到插值指令,该指令将变量值合并到HTML页面中。此绑定在$scope.data.time variable
。
该示例还包含两个按钮。第一个按钮附有一个ng-click
侦听器。单击该按钮时,将调用$scope.updateTime()
函数,然后AngularJS调用$scope.$digest()
以便更新数据绑定。
第二个按钮从控制器函数内部获取一个标准的JavaScript事件监听器。单击第二个按钮时,将执行侦听器功能。如您所见,两个按钮的侦听器函数几乎相同,但是当调用第二个按钮的侦听器函数时,不会更新数据绑定。这是因为在执行第二个按钮的事件侦听器后未调用$scope.$digest()
。因此,如果单击第二个按钮,时间将在$scope.data.time
变量中更新,但永远不会显示新时间。
要解决这个问题,我们可以在按钮事件监听器的最后一行添加$scope.$digest()
调用,如下所示:
document.getElementById("updateTimeButton")
.addEventListener('click', function() {
console.log("update time clicked");
$scope.data.time = new Date();
$scope.$digest();
});
您可以使用$digest()
函数,而不是在按钮侦听器函数中调用$apply()
:
document.getElementById("updateTimeButton")
.addEventListener('click', function() {
$scope.$apply(function() {
console.log("update time clicked");
$scope.data.time = new Date();
});
});
注意如何从按钮事件侦听器内部调用$scope.$apply()
函数,以及如何在作为参数传递给$scope.data.time
函数的函数内执行$apply()
变量的更新。当$apply()
函数调用在内部完成AngularJS调用$digest()
时,所有数据绑定都会更新。
答案 3 :(得分:44)
AngularJS扩展了这个 events-loop ,创建了一个名为AngularJS context
的东西。
<强> $手表()强>
每次在UI中绑定某些内容时,都会在$watch
列表中插入 $watch
。
User: <input type="text" ng-model="user" />
Password: <input type="password" ng-model="pass" />
这里我们有$scope.user
,它绑定到第一个输入,我们有$scope.pass
,它绑定到第二个输入。为此,我们将两个 $watch
es添加到$watch
列表。
当加载我们的模板时,AKA处于链接阶段,编译器将查找每个指令并创建所需的所有$watch
。
AngularJS提供$watch
,$watchcollection
和$watch(true)
。下面是一张简洁的图表,解释了从watchers in depth获取的所有三个。
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();
});
}
$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不会创建任何手表。
我们按下按钮。
$digest
循环将运行,并会询问每个$ watch的更改。$watch
正在关注$ scope.name中的更改
报告更改,它将强制执行另一个$digest
循环。$digest
循环。这意味着每次我们在输入中写一个字母时,循环都会检查此页面中的每个$watch
。如果你在事件被触发时调用$apply
,它将通过角度上下文,但如果你不调用它,它将在它之外运行。就是这么简单。 $apply
将在内部调用$digest()
循环,它将遍历所有监视以确保使用新更新的值更新DOM。
$apply()
方法会触发整个$scope
链上的观察者,而$digest()
方法只会触发当前$scope
及其children
上的观察者。 当高层$scope
个对象都不需要了解本地更改时,您可以使用$digest()
。
答案 4 :(得分:17)
还有$watchGroup
和$watchCollection
。具体来说,$watchGroup
非常有用,如果你想调用一个函数来更新一个在一个非dom对象的视图中有多个属性的对象,例如, canvas,webGL或服务器请求中的其他视图。这里是文档link。
答案 5 :(得分:17)
我发现了非常深入的视频,其中涵盖了application.html.erb
<!DOCTYPE html>
<html>
<head>
<title><%= full_title(yield(:title)) %></title>
<%= stylesheet_link_tag 'application', media: 'all',
'data-turbolinks-track' => true %>
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
<%= csrf_meta_tags %>
<meta name="viewport" content="width=device-width, initial-scale=1">
<%= render 'layouts/shim' %>
</head>
<body>
<%= render 'layouts/header' %>
<div class="container-fluid">
</div>
</body>
</html>
application.js
//= require jquery2
//= require jquery_ujs
//= require jquery-ui/autocomplete
//= require autocomplete-rails
//= require dropzone
//= require select2-full
//= require bootstrap
//= require turbolinks
//= require_tree .
//= require selectize
application.css
*= require jquery-ui/autocomplete
*= require dropzone/dropzone
*= require select2
*= require select2-bootstrap
*= require selectize
*= require selectize.default
*= require_tree .
*= require_self
*/
,$watch
,$apply
和摘要周期:
AngularJS - Understanding Watcher, $watch, $watchGroup, $watchCollection, ng-change
AngularJS - Understanding digest cycle (digest phase or digest process or digest loop)
AngularJS Tutorial - Understanding $apply and $digest (in depth)
以下是在这些视频中使用的几张幻灯片来解释这些概念(以防万一,如果以上链接被删除/无效)。
在上图中,“$ scope.c”未被监视,因为它未在任何数据绑定中使用(在标记中)。其他两个($digest
和$scope.a
)将会被观看。
从上图:根据各自的浏览器事件,AngularJS捕获事件,执行摘要周期(通过所有手表进行更改),执行监视功能并更新DOM。如果不是浏览器事件,则可以使用$scope.b
或$apply
手动触发摘要周期。
有关$digest
和$apply
的更多信息:
答案 6 :(得分:12)
刚读完以上所有内容,无聊而困倦(抱歉,但这是真的)。非常技术性,深入,细致,干燥。 我为什么要写作?因为AngularJS是庞大的,许多相互关联的概念可以让任何人变得疯狂。我经常问自己,我是不是很聪明才能理解它们?没有!这是因为很少有人可以用 for dummie language 来解释所有术语的技术! 好的,让我试试:
1)它们都是事件驱动的东西。(我听到笑声,但请继续阅读)
如果你不知道什么是事件驱动的话 想你放一个按钮 在页面上,使用&#34;点击&#34;等待,将其连接到一个功能 用户点击它来触发你在里面种植的动作 功能。或者想想&#34;触发&#34; SQL Server / Oracle。
2)$ watch&#34;点击&#34;。
它的特殊之处在于它需要2个函数作为参数,第一个参数 给出事件的值,第二个将值带入 考虑...
3)$ digest是不知疲倦地检查的老板, bla-bla-bla但是一个好老板。
4)$ apply为您提供了手动操作的方式,就像防故障一样(如果点击不启动,则强制它运行) 。)
现在,让我们视觉化。想象一下,这样可以更容易理解这个想法:
在餐厅
- WAITERS 应该接受客户的订单,这是
$watch(
function(){return orders;},
function(){Kitchen make it;}
);
- MANAGER 跑来跑去确保所有服务员都醒着,以应对客户的任何变化迹象。这是$digest()
- OWNER 具有根据要求驱动每个人的最终力量,这是$apply()