Angular让我们将我们的界面的部分绑定到JavaScript代码中的数据,但是如何知道数据何时更新,页面需要更新?
一种策略是使用特殊对象,其中通过方法设置数据,而不是属性分配。然后可以注意到更改,可以更新页面。这有缺点,我们必须extend一些特殊的对象。另外,为了分配,我们必须使用更详细的obj.set('key','value')
形式,而不是obj.key ='value'
。像EmberJS和KnockoutJS这样的框架使用这个策略。
Angular采用不同的方法:允许任何值作为绑定目标。然后在任何JavaScript代码return的末尾,检查该值是否已更改。这可能似乎在第一次失效,但有一些聪明的策略来降低性能耗损。最大的好处是我们可以使用普通对象并更新我们所需要的数据,而且这些变化将被注意到并反映在我们的绑定中。
为了实现这一策略,我们需要知道数据何时发生变化,而这也正是$scope.$apply派上用场的地方。
$apply and $digest
假设我们需要在2000ms之后更新一个值到页面,你可能会这样实现你的代码:
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>scope()</title> <script src="https://code.angularjs.org/angular-1.0.1.min.js"></script> </head> <body> <div ng:app ng-controller="Ctrl"> {{message}} </div> <script> function Ctrl($scope) { $scope.message = "Waiting 2000ms for update"; setTimeout(function() { $scope.message = "Timeout called!"; // AngularJS unaware of update to $scope }, 2000); } </script> </body> </html>
页面并不会像你预期的那样得到更新,此时可以用$scope.$apply()来改写上面的代码
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>scope()</title> <script src="https://code.angularjs.org/angular-1.0.1.min.js"></script> </head> <body> <div ng:app ng-controller="Ctrl"> {{message}} </div> <script> function Ctrl($scope) { $scope.message = "Waiting 2000ms for update"; setTimeout(function() { $scope.$apply(function() { $scope.message = "Timeout called!"; }); }, 2000); } </script> </body> </html>
当$scope.$apply()执行的时候,Angular会触发$scope.$digest()去更新bindings 或者 watchers。Angular中ng-click、controller初始化都会自动执行$apply(),甚至$http callbacks也被包含在$scope.$apply()中。
Angular提供了类似setTimeout的方法$timeout,$timeout自动将你的代码包裹在$apply中。因此上面的代码可以改写为:
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>scope()</title> <script src="https://code.angularjs.org/angular-1.0.1.min.js"></script> </head> <body> <div ng:app ng-controller="Ctrl"> {{message}} </div> <script> function Ctrl($scope,$timeout) { $scope.message = "Waiting 2000ms for update"; $timeout(function() { $scope.message = "Timeout called!"; }, 2000); } </script> </body> </html>