[转载]AngularJS and scope.$apply

http://jimhoskins.com/2012/12/17/angularjs-and-apply.html

http://www.cnblogs.com/zhrj000/p/3383898.html

If you’ve written a non-trivial amount of code in AngularJS, you may have come across the $scope.$apply() method. On the surface, it may seem like just a method you call to get your bindings to update. But why does it exist? And when do you need to use it?

To really understand when to use $apply, it’s good to know exactly why we need to use it, so let’s dive in!

JavaScript is Turn Based

The JavaScript code we write doesn’t all run in one go, instead it executes in turns. Each of these turns runs uninterupted from start to finish, and when a turn is running, nothing else happens in our browser. No other JavaScript code runs, and our web page interface is completely frozen. This is why poorly coded JavaScript can freeze a web page.

Instead, whenever there is a task that takes some amount of time, such as an Ajax request, waiting for a click event, or setting a timeout, we set up a callback function and finish our current turn. Later, when the Ajax request completes, a click is detected, or the timer completes, a new JavaScript turn is created and the callback is run to completion.

Let’s look at an example JavaScript file:

var button = document.getElementById(‘clickMe‘);

function buttonClicked () {
  alert(‘the button was clicked‘);
}

button.addEventListener(‘click‘, buttonClicked);

function timerComplete () {
  alert(‘timer complete‘);
}

setTimeout(timerComplete, 2000);

When the JavaScript code is loaded, that is a single turn. It finds a button, adds a click listener, and sets a timeout. Then the turn is complete, and the browser will update the web page if necessary, and begin accepting user input.

If the browser detects a click on #clickMe, it creates a new turn, which executes the buttonClicked function. When that function returns, that turn is complete.

After 2000 milliseconds, the browser creates a new turn which calls timerComplete.

Our JavaScript code is run in turns, and in between the turns is when the page is repainted, and input is accepted.

How do we update bindings?

So Angular lets us bind parts of our interface to data in our JavaScript code, but how does it know when data changes, and the page needs updating?

There are a few solutions. The code needs to know when a value has changed. Right now there is no way for our code to be directly notified of changes on an object 1. Instead there are two main strategies.

One strategy is to use special objects, where data is set via methods, not property assignments. Then changes can then be noted, and the page can be updated. This has the downside in that we must extend some special object. Also, for assigning, we must use a more verbose form obj.set(‘key‘, ‘value‘) instead of obj.key = ‘value‘. Frameworks like EmberJS and KnockoutJS use this strategy.

AngularJS takes a different approach: allow any value to be used as a binding target. Then at the end of any JavaScript code turn, check to see if the value has changed. This may seem inneficient at first, but there are some clever strategies to reduce the performance hit. The big benefit is we can use normal objects and update our data however we want, and the changes will be noticed and reflected in our bindings.

For this strategy to work, we need to know when data has possibly changed, and this is where $scope.$apply comes into play.

$apply and $digest

That step that checks to see if any binding values have changed actually has a method, $scope.$digest(). That’s actually where the magic happens, but we almost never call it directly, instead we use $scope.$apply() which will call $scope.$digest() for you.

$scope.$apply() takes a function or an Angular expression string, and executes it, then calls $scope.$digest() to update any bindings or watchers.

So, when do you need to call $apply()? Very rarely, actually. AngularJS actually calls almost all of your code within an $apply call. Events like ng-click, controller initialization, $http callbacks are all wrapped in $scope.$apply(). So you don’t need to call it yourself, in fact you can’t. Calling $apply inside $apply will throw an error.

You do need to use it if you are going to run code in a new turn. And only if that turn isn’t being created from a method in the AngularJS library. Inside that new turn, you should wrap your code in $scope.$apply(). Here is an example. We are using setTimeout, which will execute a function in a new turn after a delay. Since Angular doesn’t know about that new turn, the update will not be reflected.

But, if we wrap the code for that turn in $scope.$apply(), the change will be noticed, and the page is updated.

As a convenience, AngularJS provides $timeout, which is like setTimeout, but automatically wraps your code in $apply by default. Use that, not this

If you write any code that uses Ajax without $http, or listens for events without using Angular’s ng-* listeners, or sets a timeout without $timeout, you should wrap your code in $scope.$apply

$scope.$apply() vs $scope.$apply(fn)

Sometimes I see examples where data is updated, and then $scope.$apply() is called with no arguments. This achieves the desired result, but misses some opportunities.

If your code isn’t wrapped in a function passed to $apply, and it throws an error, that error is thrown outside of AngularJS, which means any error handling being used in your application is going to miss it. $apply not only runs your code, but it runs it in a try/catch so your error is always caught, and the $digest call is in a finally clause, meaning it will run regardless of an error being thrown. That’s pretty nice.

Hopefully now you understand what $apply is and when to use it. If you only use what AngularJS provides you, you shouldn’t need to use it often. But if you begin writing directives where you are observing DOM elements directly, it is going to become necessary.



Object.observe has been proposed for ES5, but is only experimentally implemented now

时间: 2024-10-15 15:00:56

[转载]AngularJS and scope.$apply的相关文章

[转载]AngularJS入门教程02:AngularJS模板

是时候给这些网页来点动态特性了——用AngularJS!我们这里为后面要加入的控制器添加了一个测试. 一个应用的代码架构有很多种.对于AngularJS应用,我们鼓励使用模型-视图-控制器(MVC)模式解耦代码和分离关注点.考虑到这一点,我们用AngularJS来为我们的应用添加一些模型.视图和控制器. 请重置工作目录: git checkout -f step-2 我们的应用现在有了一个包含三部手机的列表. 步骤1和步骤2之间最重要的不同在下面列出.,你可以到GitHub去看完整的差别. 视图

[转载]AngularJS入门教程00:引导程序

我们现在开始准备编写AngularJS应用——phonecat.这一步骤(步骤0),您将会熟悉重要的源代码文件,学习启动包含AngularJS种子项目的开发环境,并在浏览器端运行应用. 进入angular-phonecat目录,运行如下命令: git checkout -f step-0 该命令将重置phonecat项目的工作目录,建议您在每一学习步骤运行此命令,将命令中的数字改成您学习步骤对应的数字,该命令将清除您在工作目录内做的任何更改. 运行以下命令: node scripts/web-s

[转载]AngularJS入门教程01:静态模板

为了说明angularJS如何增强了标准HTML,我们先将创建一个静态HTML页面模板,然后把这个静态HTML页面模板转换成能动态显示的AngularJS模板. 在本步骤中,我们往HTML页面中添加两个手机的基本信息,用以下命令将工作目录重置到步骤1. git checkout -f step-1 请编辑app/index.html文件,将下面的代码添加到index.html文件中,然后运行该应用查看效果. app/index.html <ul> <li> <span>

[转载]AngularJS入门教程04:双向绑定

在这一步你会增加一个让用户控制手机列表显示顺序的特性.动态排序可以这样实现,添加一个新的模型属性,把它和迭代器集成起来,然后让数据绑定完成剩下的事情. 请重置工作目录: git checkout -f step-4 你应该发现除了搜索框之外,你的应用多了一个下来菜单,它可以允许控制电话排列的顺序. 步骤3和步骤4之间最重要的不同在下面列出.你可以在GitHub里看到完整的差别. 模板 app/index.html Search: <input ng-model="query"&g

[转载]AngularJS入门教程03:迭代器

我们在上一步做了很多基础性的训练,所以现在我们可以来做一些简单的事情喽.我们要加入全文检索功能(没错,这个真的非常简单!).同时,我们也会写一个端到端测试,因为一个好的端到端测试可以帮上很大忙.它监视着你的应用,并且在发生回归的时候迅速报告. 请重置工作目录: git checkout -f step-3 我们的应用现在有了一个搜索框.注意到页面上的手机列表随着用户在搜索框中的输入而变化. 步骤2和步骤3之间最重要的不同在下面列出.你可以在GitHub里看到完整的差别. 控制器 我们对控制器不做

angularJS $watch $digest $apply

一 简介AngularJS提供了一个非常酷的特性叫做双向数据绑定(Two-way Data Binding),这个特性大大简化了我们的代码编写方式.数据绑定意味着当View中有任何数据发生了变化,那么这个变化也会自动地反馈到scope的数据上,也即意味着scope模型会自动地更新.类似地,当scope模型发生变化时,view中的数据也会更新到最新的值.那么AngularJS是如何做到这一点的呢?当你写下表达式如{{ aModel }}时,AngularJS在幕后会为你在scope模型上设置一个w

angularJS中的$apply(),$digest(),$watch()

$apply()和$digest()在AngularJS中是两个核心概念,但是有时候它们又让人困惑.而为了了解AngularJS的工作方式,首先需要了解$apply()和$digest()是如何工作的. $apply()和$digest() AngularJS提供了一个非常酷的特性叫做双向数据绑定(Two-way Data Binding),这个特性大大简化了我们的代码编写方式.数据绑定意味着当View中有任何数据发生了变化,那么这个变化也会自动地反馈到scope的数据上,也即意味着scope模

angularJS报错$apply already in progress的原因和解决方法

如果我们使用了AngularJS中的$scope.$apply()或者$scope.$digest(),我们很可能会遇到类似下面的错误,虽然这个错误没有太大影响,但是在日志中看起来还是很不爽的,日志中记录的异常或者错误,就应该是需要关注和解决的问题,否则就没有必要出现在日志中了. 原因是:angularjs框架本身已经在做脏数据检测了,我们没有必要再手动调用$apply或者$digest.这里自然而然出现了一个疑问:什么时候需要我们手动调用$apply或者$digest,什么时候不需要呢?先列出

angularJS学习小结——$apply方法和$watch方法

引言 最近在项目中封装控件的时候用到了$watch方法来监听module中的值的变化,当时小编对这个方法不是很了 解,所以在网上找了一些资料来学习一下,下面小编就给大家简单介绍一些angularJS中Scope 提供$apply 方法传播 Model 的变化和$watch方法监听module变化. $apply使用情景 AngularJS 外部的控制器(DOM 事件.外部的回调函数如 jQuery UI 空间等)调用了AngularJS 函数之后,必 须调用$apply.在这种情况下,你需要命令