本节书摘来自华章出版社《AngularJS实战》一 书中的第2章,第2.2节,作者:陶国荣,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
2.2 Angular中的控制器
在介绍完Angular中的表达式后,接下来再来介绍一下Angular中一个重要角色——控制器(controller)。其实,在前面的章节中我们也多次提到它,那么,Angular中的控制器到底是什么?它又能实现哪些功能呢?接下来,我们结合示例来逐一进行解析。
2.2.1 控制器的概念
控制器在Angular中占有重要的地位,它是组成前端MVC框架的一员。在Angular中,控制器的功能是管理页面的逻辑代码。当控制器通过“ng-controller”指令被添加到DOM页面时,Angular将会通过控制器构造函数生成一个实体的对象,而在生成这个对象的过程中,$scope对象作为参数注入其中,并允许用户访问$scope对象。这样一来,用户可以通过$scope对象与页面中的元素进行数据绑定,从而实现数据从控制器层到视图层的过程。
2.2.2 控制器初始化$scope对象
从上面对控制器的介绍,我们不难看出,控制器的任务就是操作$scope对象,而这种操作具体表现在两个方面:一是对$scope对象进行初始化,二是为$scope对象添加各种实现逻辑功能的方法。我们先来看第一个功能,初始化$scope对象。
先通过一个完整的示例来介绍控制器初始化$scope对象的过程。
示例2-4 控制器初始化$scope对象
(1)功能描述
在页面中,以应用模块的方法构建一个控制器对象,并以内联声明方式表明控制器对象依赖于$scope对象提供的服务。在控制器对象中,初始化$scope对象的名为“text”的属性,并与页面的元素进行数据绑定。
(2)实现代码
新建一个HTML文件2-4.html,加入如代码清单2-4所示的代码。
代码清单2-4 控制器初始化$scope对象
<!doctype html>
<html ng-app="a2_4">
<head>
<title>控制器初始化$scope对象</title>
<script src="../Script/angular.min.js"
type="text/javascript"></script>
</head>
<body>
<div ng-controller="c2_4">
<span>{{text}}</span>
</div>
<script type="text/javascript">
var a2_4 = angular.module('a2_4', []);
a2_4.controller('c2_4', ['$scope', function ($scope) {
$scope.text = 'Hello!Angular';
}]);
</script>
</body>
</html>
(3)页面效果
执行HTML文件2-4.html,最终实现的页面效果如图2-4所示。
(4)源码分析
在本示例的源码中,自定义控制器函数的创建由全局方式改为在Angular模块下使用. controller方式,这种方式更加强调页面是一个整体的应用,控制器可以对应用中的某一个模块进行管理。相对于全局方式下的定义,该方式的扩展性和针对性更强。
在本示例中,首先定义了一个名为“a2_4”的Angular模块,然后,通过下列代码,创建了一个名为“c2_4”的控制器。
a2_4.controller('c2_4', ['$scope', function ($scope) {
...代码块
}]);
在上述的代码中,采用内联注入的方式声明“c2_4”控制器的构建依赖Angular中的$scope对象,即在构建控制器时,Angular会自动将$scope对象作为参数注入到控制器中。
在构建控制器函数时,虽然$scope对象已经自动注入,但还是需要对它进行初始化。初始化的方法是向该对象添加属性,在本示例中的代码如下。
$scope.text = 'Hello!Angular';
采用上述方式也可以添加多个属性,被添加的这些属性在控制器所管理的所有DOM元素中都可以采用数据绑定的方式进行访问。例如, 对于下列代码:
<div ng-controller="c2_4">
<span>{{text}}</span>
</div>
通过元素的“ng-controller”属性值指定控制器函数的名称,表明该元素中的全部DOM节点都受控制器管理,然后,采用双花括号的方式绑定控制器的数据,从而最终实现View层数据展示的功能。
2.2.3 添加$scope对象方法
除了可以通过初始化的方式为$scope对象添加属性之外,还可以为$scope对象添加方法,并依靠这些定义的方法来满足视图层中逻辑处理和事件执行的需要。在添加完$scope对象的方法函数之后,在视图层中,就可以像绑定属性一样,通过表达式的方式绑定这些函数,处理业务逻辑需求,也可以通过Angular的事件处理器,将执行的事件(如ng-click)绑定给这些函数,用来实现事件触发时需要完成的功能需求。
接下来,我们分别通过两个示例来介绍添加并执行$scope对象方法的过程。
示例2-5 通过表达式绑定$scope对象的方法
(1)功能描述
在示例2-4的基础上向$scope对象添加一个名为“span_show”的方法函数。在该函数中,先重置$scope对象的“text”属性值,并通过return方法返回重置后的内容值;在页面中,通过Angular表达式绑定$scope对象中的“span_show”函数,显示重置后返回的内容。
(2)实现代码
新建一个HTML文件2-5.html,加入如代码清单2-5所示的代码。
代码清单2-5 控制器初始化$scope对象
<!doctype html>
<html ng-app="a2_5">
<head>
<title>通过表达式绑定$scope对象的方法</title>
<script src="../Script/angular.min.js"
type="text/javascript"></script>
</head>
<body>
<div ng-controller="c2_5">
<span class="show">{{span_show()}}</span>
</div>
<script type="text/javascript">
var a2_5 = angular.module('a2_5', []);
a2_5.controller('c2_5', ['$scope', function ($scope) {
$scope.text = 'Hello!Angular';
$scope.span_show = function () {
$scope.text = "欢迎来到 Angular 的精彩世界!";
return $scope.text;
};
}]);
</script>
</body>
</html>
(3)页面效果
执行HTML文件2-5.html,最终实现的页面效果如图2-5所示。
(4)源码分析
在本示例的源码中,在构建名为“c2_5”的控制器时,除添加“text”属性外,还向$scope对象添加了一个名为“span_show”的方法。该方法是一个自定义的函数,在函数中先重新设置了$scope对象的“text”属性值,再通过return语句返回重置后的属性值。
为了能在页面中执行在控制器中定义的“span_show”方法,在双花括号中以表达式的方式直接调用方法名称,因为调用该方法将返回重置后的$scope对象的“text”属性值,所以,在页面的元素中,显示$scope对象被重置后的字符内容,完整效果如图2-5所示。
除了在页面中,通过Angular的表达式调用$scope对象的方法外,还可以通过Angular的事件处理器将方法与某个事件绑定,在触发事件时执行已绑定的方法。
示例2-6 在事件中绑定$scope对象的方法
(1)功能描述
在示例2-4的基础上向$scope对象添加一个名为“click_show”
的方法,同时,在页面中添加一个<button>
元素,并在元素的click
事件中绑定该方法。当单击按钮时,在页面的<span>
元素中显示“单击后显示的内容!”字样。
(2)实现代码
新建一个HTML文件2-6.html,加入如代码清单2-6所示的代码。
代码清单2-6 在事件中绑定$scope对象的方法
<!doctype html>
<html ng-app="a2_6">
<head>
<title>在事件中绑定$scope对象的方法</title>
<script src="../Script/angular.min.js"
type="text/javascript"></script>
</head>
<body>
<div ng-controller="c2_6">
<span class="show">{{text}}</span>
<input id="btnShow" type="button"
ng-click="click_show();" value="显示" />
</div>
<script type="text/javascript">
var a2_6 = angular.module('a2_6', []);
a2_6.controller('c2_6', ['$scope', function ($scope) {
$scope.text = 'Hello!Angular';
$scope.click_show = function () {
$scope.text = "单击后显示的内容!";
};
}]);
</script>
</body>
</html>
(3)页面效果
执行HTML文件2-6.html,最终实现的页面效果如图2-6所示。
(4)源码分析
在本示例的源码中,在构建控制器时,向$scope
对象添加了一个名为“click_show”
的方法。在该方法中,将$scope
对象的“text”
属性重新赋值;在页面中,将“click_show”
方法与<button>
元素的click
事件通过Angular
中的事件处理器“ng-click”
相绑定,这样在单击<button>
按钮时触发click
事件,并调用已绑定的“click_show”
方法,重置$scope
对象的“text”
属性值。
由于在<span>
元素中通过双花括号与“text”
属性值进行了双向绑定,因此,一旦重置“text”
属性值完成,被绑定的页面内容也将自动进行同步变更,最终,在页面的<span>
中显示了“单击后显示的内容!”字样,其效果如图2-6所示。
除了可以向$scope对象添加方法之外,还可以在方法中添加参数,多个参数同样通过逗号进行隔开。接下来,我们再通过一个示例来介绍向$scope对象添加带参数的方法。
示例2-7 添加带参数的$scope方法
(1)功能描述
在示例2-6的基础上再向控制器中添加一个名为“click_para”的带参数方法,并在页面中再添加一个<button>
元素,并且将新添方法与元素的“ng-click”事件绑定,这样当单击该元素时,在元素中显示被绑定方法的参数内容。
(2)实现代码
新建一个HTML文件2-7.html,加入如代码清单2-7所示的代码。
代码清单2-7 添加带参数的$scope方法
<!doctype html>
<html ng-app="a2_7">
<head>
<title>添加带参数的$scope方法</title>
<script src="../Script/angular.min.js"
type="text/javascript"></script>
</head>
<body>
<div ng-controller="c2_7">
<span class="show">{{text}}</span>
<input id="btnShow" type="button"
ng-click="click_show();" value="显示" />
<input id="btnPara" type="button"
ng-click="click_para('单击了带参数按钮!');"
value="带参数显示" />
</div>
<script type="text/javascript">
var a2_7 = angular.module('a2_7', []);
a2_7.controller('c2_7', ['$scope', function ($scope) {
$scope.text = 'Hello!Angular';
$scope.click_show = function () {
$scope.text = "单击后显示的内容!";
};
$scope.click_para = function (ptext) {
$scope.text = ptext;
};
}]);
</script>
</body>
</html>
(3)页面效果
执行HTML文件2-7.html,最终实现的页面效果如图2-7所示。
(4)源码分析
在本示例的源码中,在构建控制器代码时,新添加一个名为“click_para”
的带参方法,在该方法中将形参ptext
的值设置为$scope
对象“text”
的属性值;而在页面中通过Angular的事件处理器,将新添加的方法与另外一个<button>
元素的click
事件相绑定。在绑定时,将字符串常量“单击了带参数按钮!”作为调用方法时需要传递的实参。
当单击<button>
元素时便触发了click
事件,在事件中调用“click_para”
方法,由形参ptext
接收传来的字符串常量,并将该常量值作为$scope
对象“text”
的属性值。由于页面中的<span>
元素已通过双花括号中的表达式绑定了“text”
属性值,因此,在控制器中$scope
对象的“text”
属性值变化后,被绑定的<span>
元素显示内容将自动同步更新,最终将更新的内容显示在页面中。
2.2.4 $scope对象属性和方法的继承
继承,顾名思义,指的是一种层次间的延续关系。由于页面本身就是一个具有层次性的DOM结构模型,而Angular中的“ng-controller”指令也允许在不同的层次元素中指定控制器。因此,处于子层控制器中的$scope对象将会自动继承父层控制器中$scope对象的属性和方法。
接下来,我们通过一个示例来说明$scope对象中属性和方法的继承过程。
示例2-8 $scope对象中方法和属性的继承
(1)功能描述
在页面中添加两个具有包含关系的
<button>
按钮时调用父级控制器中添加的方法。(2)实现代码
新建一个HTML文件2-8.html,加入如代码清单2-8所示的代码。
代码清单2-8 $scope对象方法和属性的继承
<!doctype html>
<html ng-app="a2_8">
<head>
<title>$scope对象方法和属性的继承</title>
<script src="../Script/angular.min.js"
type="text/javascript"></script>
</head>
<body>
<div ng-controller="c2_8">
<div ng-controller="c2_8_1">
<span class="show">{{text}}</span><br />
<span class="show">{{child_text}}</span>
<input id="btnShow" type="button"
ng-click="click_show();" value="继承" />
</div>
</div>
<script type="text/javascript">
var a2_8 = angular.module('a2_8', []);
a2_8.controller('c2_8', ['$scope', function ($scope) {
$scope.text = 'Hello!Angular';
$scope.click_show = function () {
$scope.text = "单击按钮后显示的内容!";
};
}]);
a2_8.controller('c2_8_1', ['$scope', function ($scope) {
$scope.child_text = '欢迎来到 Angular 的精彩世界!';
}]);
</script>
</body>
</html>
(3)页面效果
执行HTML文件2-8.html,最终实现的页面效果如图2-8所示。
图2-8 $scope对象方法和属性的继承
(4)源码分析
在本示例的页面代码中,我们通过“a2_8”模块分别构建了两个名称为“c2_8”和“c2_8_1”的控制器,用于管理页面中的父级和子级
<button>
元素绑定了父级控制器中$scope对象的“click_show”方法,当单击该按钮时,重置“text”属性的值,并同步更新页面中绑定该属性显示的内容。在通常情况下,在$scope对象的继承中,不仅局限于父与子的层次关系,还是一种内层继承外层的顺序关系,即最内层可以继承包含它的所有外层中$scope对象的属性和方法,而在最内层控制器中添加的新属性和方法,最外层不能访问。因此,这种继承是一种由内向外的顺序关系。