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>