本节书摘来自华章出版社《AngularJS实战》一 书中的第3章,第3.3节,作者:陶国荣,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
3.3 作用域概述
在前面的章节中我们曾经介绍过$scope对象,确切来说,它的实质就是一个作用域对象。从对这个对象的使用,我们发现作用域能存储数据模型、为表达式提供上下文环境和监听表达式的变化并且传播事件,它是页面视图与控制器之间的重要桥梁,也是掌握Angular必须知道的基础概念,接下来,我们详细介绍它的基础功能和在DOM中的使用方法。
3.3.1 作用域特点
具体来说,作用域包括下列3个比较显著的特点。
它提供了一个$watch方法来监听数据模型的变化,之所以能使用“ng-model”指令实现数据的双向绑定,就是通过调用该方法进行数据模型的监听,只要有一端发生变化,另外绑定的一端自动进行数据同步。
它提供另外一个$apply方法,为各种类型的数据模型改变提供支撑,将它们引入到Angular可控制器的范围中。最典型的就是控制器,例如,通过页面视图模板中“ng-click”指令,执行控制器中的代码。
它为表达式提供了执行的环境,一个表达式必须在拥有该表达式属性的作用域中执行才更加合适,作用域通过提供$scope对象,使所有的表达式都拥有对应的执行环境,也就是执行的上下文对象。
接下来,通过一个完整的示例来介绍作用域下调用$watch方法监听数据模块的变化。
示例3-6 $watch方法的使用
(1)功能描述
当在页面的文本框中输入任意“姓名”字符时,另一个
(2)实现代码
新建一个HTML文件3-6.html,加入如代码清单3-6所示的代码。
代码清单3-6 $watch方法
<!doctype html>
<html ng-app="a3_6">
<head>
<title>$watch方法</title>
<script src="../Script/angular.min.js"
type="text/javascript"></script>
<style type="text/css">
body {
font-size: 12px;
}
div{
margin:8px 0px;
}
</style>
</head>
<body>
<div ng-controller="c3_6">
<div><input type="text"
ng-model="name" placeholder="请输入姓名"/>
</div>
<div>累计变化次数: {{count}}</div>
</div>
<script type="text/javascript">
var a3_6 = angular.module('a3_6', []);
a3_6.controller('c3_6', ['$scope', function ($scope) {
$scope.count = 0;
$scope.name = '';
$scope.$watch('name', function () {
$scope.count++;
})
}])
</script>
</body>
</html>
(3)页面效果
执行HTML文件3-6.html,最终实现的页面效果如图3-6所示。
(4)源码分析
在本示例的源码中,当在页面的控制器中编写代码时,先定义两个$scope对象的属性“name”和“count”,前者用于使用“ng-model”指令绑定文本框中的内容,后者用于记录文本框中字符内容变化的累计次数。
然后,使用作用域中的$watch方法对$scope中的“name”属性进行监视,当该属性值发生变化时,将$scope中的“count”属性值累加1,所以,只要在文本输入框中做任何一次的修改,都会通过“count”属性值反馈至页面中。
之所以可以通过$watch方法监控模型数据发生的变化,主要是因为在Angular的内部,每当我们对已绑定“ng-model”指令的“name”属性进行修改时,其内部的$digest方法就会自动运行一次,检测已绑定的“name”属性是否与上一次$digest方法运行时获取的内容一致。如果不一致,则执行$watch方法绑定的处理函数,即将“count”属性值累加1。
3.3.2 作为数据模型的作用域
在前面的章节中我们曾说过,作用域是控制器与视图的桥梁。不仅如此,它也是视图和指令的桥梁。因为在自定义指令时,会调用$watch方法监听各个表达式的变化,一旦作用域中的表达式发生了变化,$watch方法将通知指令,而指令将根据这个变化重新渲染DOM页面,即更新作用域中的属性值内容。
无论是指令,还是控制器,它们都可以通过作用域与视图中的DOM绑定。由此,诞生了两个数据关系链,一条是:指令——>作用域——>视图;另一条是:控制器——>作用域——>视图。这两条关系链之间还是相互独立的。
接下来,通过一个简单的示例来介绍控制器借助作用域控制视图中元素的显示内容。
示例3-7 作为数据模型的作用域
(1)功能描述
当在页面的文本框中输入内容时,
(2)实现代码
新建一个HTML文件3-7.html,加入如代码清单3-7所示的代码。
代码清单3-7 作为数据模型的作用域
<!doctype html>
<html ng-app="a3_7">
<head>
<title>作为数据模型的作用域</title>
<script src="../Script/angular.min.js"
type="text/javascript"></script>
<style type="text/css">
body {
font-size: 12px;
}
div{
margin:8px 0px;
}
</style>
</head>
<body>
<div ng-controller="c3_7">
<div><input type="text"
ng-model="name" placeholder="请输入姓名"/>
</div>
<div>{{name}}</div>
</div>
<script type="text/javascript">
var a3_7 = angular.module('a3_7', []);
a3_7.controller('c3_7', ['$scope', function ($scope) {
$scope.name = '';
}])
</script>
</body>
</html>
(3)页面效果
执行HTML文件3-7.html,最终实现的页面效果如图3-7所示。
(4)源码分析
在上述示例的源代码中,先通过页面模块定义了一个名为“c3_7”的控制器,在控制器中引用$scope对象注册了一个名为“name”的属性,而$scope对象则通过注册的属性,控制了页面所需的数据模型,视图模板则通过双向绑定的方式传递并显示数据模型中的属性值。
接下来,分别从控制器和视图这两个角度来分析这个示例的具体流程。从控制器的角度来说,首先,通过$scope对象为“name”属性赋初始值;然后,通过$watch方法通知视图中的文本输入框元素数据已发生了变化,而文本框元素使用“ng-model”指令实现了数据的双向绑定。因此,它可以获悉该变化,并自动同步变化后的“name”属性值,且该值渲染至文本框中。
从视图的角度来说,由于文本框通过“ng-model”指令进行了数据的双向绑定,因此,先通过$scope对象获取控制器中“name”属性值;然后,如果用户在文本框中输入了新的内容,则会自动将该内容传递给控制器中的“name”属性,保持两端的数据同步。
此外,在