AngularJS实战
上QQ阅读APP看书,第一时间看更新

第2章 Angular基础知识

通过上一章对Angular的简单介绍,相信读者对使用Angular开发Web应用有了一个大致的了解。由于Angular是一个全新的开发工具,因此需要掌握许多必要的基础概念。本章将从最底层的知识讲起,由浅入深地带领大家了解并掌握Angular开发工具中的表达式、控制器、模板和模板中的过滤器这些必须了解的基础知识。

2.1 Angular中的表达式

在Angular中,表达式是运用在视图中的一个段代码,例如在第1章的示例1-2中,在计算两个数据的和时,双花括号中就是一个数值表达式,其中的值是通过调用$parse服务模块进行解析的。如果需要格式化表达式中的值,也可以调用Angular中的过滤器,例如在第1章示例1-2中,管道符“|”之后的“number:0”则调用了整数型过滤器。

2.1.1 Angular表达式与JavaScript表达式的区别

虽然都是表达式,但Angular中的表达式与传统的JavaScript中的表达式还是有明显的区别,具体表现在以下几个方面。

❑Angular中所有表达式的值都来源于$scope对象,由该对象以添加属性的方式统一进行设置,并不像在传统的JavaScript中,可以由全局的window对象来调用表达式。

❑Angular中表达式的容错能力很强,可以允许值出现null和undef ined情况,而不会像在传统JavaScript中那样抛出异常代码。

❑由于Angular中表达式的值的来源固定,因此,在表达式中,不允许出现各类判断和循环语句,这一点也与传统的JavaScript区别较大,在使用时需要注意。

❑Angular中表达式的值可以是使用管道符“|”进行格式化显示的数值,这也是不同于传统的JavaScript中表达式的一个很明显的特征。

既然Angular中的表达式与传统的JavaScript中的表达式区别很大,那么,两者之间可以相互调用吗?答案是肯定的,如果在Angular中的表达式要调用传统的JavaScript代码,需要在控制器中定义一个方法,然后由表达式调用该方法即可;而如果在传统的JavaScript代码中执行Angular中的表达式,则需要借助$eval()方法。

接下来,我们通过一个完整的示例来介绍Angular表达式与JavaScript表达式两者之间的相互调用过程。

示例2-1 Angular表达式与JavaScript表达式之间的相互调用

(1)功能描述

在页面中,当单击“计算”按钮时,将调用控制器中的“testExp1”方法,获取第一个文本框中的值并加1,然后将结果显示在浏览器的控制台中;当改变第二个文本中的Angular表达式内容时,调用$eval方法,将该表达式的结果显示在<span>元素中。

(2)实现代码

新建一个HTML文件2-1.html,加入如代码清单2-1所示的代码。

代码清单2-1 Angular表达式与JavaScript表达式之间的相互调用

    <!doctype html>
    <html ng-app>
    <head>
        <title>Angular与JavaScript表达式之间的相互调用</title>
        <script src="../Script/angular.min.js"
                 type="text/javascript"></script>
    </head>
    <body>
        <div ng-controller="c2_1">
            执行JavaScript表达式:<br />
            <input type='text' ng-model="expr1" />
            <button ng-click="testExp1(expr1)">计算</button>
            <br /><br />
            执行Angular表达式:<br />
            <input type='text' ng-model="expr2" />
            <span ng-bind="$eval(expr2)"></span>
        </div>
        <script type="text/javascript">
            function c2_1($scope) {
                  $scope.expr1 = 20;
                  $scope.expr2 = '20+1|number:0';
                  $scope.testExp1 = function (expr) {
                      var newv = parseInt(expr) + 1;
                      console.log(newv);
                  }
              }
        </script>
    </body>
    </html>

(3)页面效果

执行HTML文件2-1.html,最终实现的页面效果如图2-1所示。

图2-1 Angular与JavaScript表达式之间的相互调用

(4)源码分析

在本示例的源码中,为了在单击“计算”按钮时能执行JavaScript表达式,我们在控制器中自定义了一个名为“testExp1”的函数,该函数的功能是获取传入的数值,并将其传递给一个表达式,再将经表达式计算后的结果显示在浏览器的控制台中,这些代码全部采用传统的JavaScript编写。此外,在页面中将该函数与“计算”按钮的单击事件(ng-click)绑定,最终实现在单击该按钮时执行自定义的“testExp1”函数的功能。

接下来,在第二个文本框元素和<span>元素中,分别通过数据绑定的方式关联在控制器中设置的相应属性值,而在绑定<span>元素时,又调用了$eval方法对绑定的属性值直接进行解析。由于Angular的数据绑定是双向的,因此,当改变第二个文本框中的Angular表达式内容时,在<span>元素中执行该表达式后的结果也会自动发生变化,最终效果如图2-1所示。

2.1.2 $window窗口对象在表达式中的使用

如果在控制器中调用JavaScript代码中的全局对象window时,需要通过依赖注入的$window对象来替代window,即如果是window.alert,应该将控制器的代码修改为:$window.alert,因为每一个控制器的代码只是在它管辖的作用区域中使用。通过这样的写法,可以防止它与全局的window对象混淆,出现各类诡异的异常。

接下来,我们通过一个完整的示例来介绍$window对象使用的过程。

示例2-2 $window窗口对象在表达式中的使用

(1)功能描述

在页面中,当单击“显示”按钮时,调用控制器中的show方法,以弹出窗口的方式显示文本框中输入的内容。

(2)实现代码

新建一个HTML文件2-2.html,加入如代码清单2-2所示的代码。

代码清单2-2 $window窗口对象在表达式中的使用

    <!doctype html>
    <html ng-app>
    <head>
        <title>$window窗口对象在表达式中的使用</title>
        <script src="../Script/angular.min.js"
                 type="text/javascript"></script>
    </head>
    <body>
        <div ng-controller="c2_2">
            <input type='text' ng-model="text" />
            <button ng-click="show()">显示</button>
        </div>
        <script type="text/javascript">
            function c2_2($window,$scope) {
                  $scope.text = "";
                  $scope.show = function () {
                      $window.alert("您输入的内容是: " + $scope.text);
                  }
            }
        </script>
    </body>
    </html>

(3)页面效果

执行HTML文件2-2.htm,最终实现的页面效果如图2-2所示。

图2-2 $window窗口对象在表达式中的使用

(4)源码分析

在本示例的代码中,在自定义控制器函数c2_2时,添加了一个$window对象,用于取代全局性的window对象。在使用时,可以像访问window对象一样,调用$window对象中各类方法或属性,如alert、conf irm等。另外,在代码中,由于文本框与$scope的“text”属性进行了数据绑定,因此,只要文本框输入的内容发生变化,对应的$scope.text属性值也随之改变,所以,弹出的窗口能即时动态显示在文本框中输入的内容值。

2.1.3 Angular表达式的容错性

与传统的JavaScript表达式相比,Angular表达式的容错能力更强大,它允许表达的值为undef ined或null类型,而在传统的JavaScript表达式中,如果出现这两种类型,则会抛出“ReferenceError”类型的错误提示。

接下来,我们通过一个完整的示例来介绍Angular表达式的容错性。

示例2-3 Angular表达式的容错性

(1)功能描述

在页面中,将一个未定义的属性值与文本框绑定,另外,在单击“单击”按钮时,在浏览器的控制台中输入一个未定义的变量值,分别观察两者在页面控制台上的显示信息。

(2)实现代码

新建一个HTML文件2-3.html,加入如代码清单2-3所示的代码。

代码清单2-3 Angular表达式的容错性

    <!doctype html>
    <html ng-app>
    <head>
        <title>Angular表达式中的容错性</title>
        <script src="../Script/angular.min.js"
                 type="text/javascript"></script>
    </head>
    <body>
        <div ng-controller="c2_3">
            <span>{{tmp}}</span>
            <button ng-click="error();">单击</button>
        </div>
        <script type="text/javascript">
            function c2_3($scope) {
                $scope.temp = "Angular";
                $scope.error = function () {
                      console.log(tmp);
                }
            }
        </script>
    </body>
    </html>

(3)页面效果

执行HTML文件2-3.html,最终实现的页面效果如图2-3所示。

图2-3 Angular表达式中的容错性

(4)源码分析

在本示例中,虽然页面中的<span>元素绑定了一个未定义的Angular表达式,但由于该表达式具有很好的容错性,允许使用未定义或空值表达式,因此,浏览器的控制台并没有异常信息显示。

当单击页面中的“单击”按钮时,将执行在控制器中自定义的error函数,在该函数中将一个未定义的变量“tmp”输出至控制台。从控制台显示的信息可以看出,由于未定义被打印的变量“tmp”,因此,使用未定义的变量将在JavaScript代码中抛出“ReferenceError”类型异常,详细效果如图2-3所示。

通过上面几个示例的介绍,我们明显感觉Angular表达式的性能要比传统的JavaScript强大不少。但在实际使用Angular工具开发应用时,如果在页面中使用表达式,不能将逻辑性的判断语句或循环语句写入表达式中,因为在Angular中,这类页面的应用逻辑必须写在管理页面的控制器代码中,而不是直接写在页面的表达式中,这点需要在使用Angular表达式时注意。

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”的属性,并与页面的<span>元素进行数据绑定。

(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所示。

图2-4 控制器初始化$scope对象

(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节点都受控制器管理,然后,采用双花括号的方式绑定控制器的数据,从而最终实现Vi e w层数据展示的功能。

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所示。

图2-5 通过表达式绑定$scope对象的方法

(4)源码分析

在本示例的源码中,在构建名为“c2_5”的控制器时,除添加“text”属性外,还向$scope对象添加了一个名为“span_show”的方法。该方法是一个自定义的函数,在函数中先重新设置了$scope对象的“text”属性值,再通过return语句返回重置后的属性值。

为了能在页面中执行在控制器中定义的“span_show”方法,在双花括号中以表达式的方式直接调用方法名称,因为调用该方法将返回重置后的$scope对象的“text”属性值,所以,在页面的<span>元素中,显示$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所示。

图2-6 在事件中绑定$scope对象的方法

(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”事件绑定,这样当单击该元素时,在<span>元素中显示被绑定方法的参数内容。

(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所示。

图2-7 添加带参数的$scope方法

(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)功能描述

在页面中添加两个具有包含关系的<div>元素,在父级<div>的控制器中添加属性和方法,而将这些属性和方法与子级<div>中的元素相绑定,在子级<div>的<span>元素中显示属性内容,在单击<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”的控制器,用于管理页面中的父级和子级<div>元素。由于子级<div>元素继承了父级控制器中$scope对象的属性和方法,因此,在子级<div>元素中,第一个<span>元素绑定了父级中$scope对象的“text”属性值,<button>元素绑定了父级控制器中$scope对象的“click_show”方法,当单击该按钮时,重置“text”属性的值,并同步更新页面中绑定该属性显示的内容。

在通常情况下,在$scope对象的继承中,不仅局限于父与子的层次关系,还是一种内层继承外层的顺序关系,即最内层可以继承包含它的所有外层中$scope对象的属性和方法,而在最内层控制器中添加的新属性和方法,最外层不能访问。因此,这种继承是一种由内向外的顺序关系。

2.3 Angular中的模板

Angular自身提供了一整套完整的模板系统,配合$scope对象和数据双向绑定机制,将页面纯静态元素经过行为、属性的添加和格式的转换,最终变成在浏览器中显示的动态页。

在模板系统中,可以包含Angular的指令、数据绑定、表单控件和过滤器,同时,在处理复杂程序时,可以构建多个模板页面作用于视图层。在主页中,再通过包含(include)的方式加载这些零散的模板页。这样做的好处在于,一次定义可多处调用,增加代码的重复使用,减少维护成本。

2.3.1 构建模板内容

构建模板的内容是使用模板功能的前提,一般通过下列几种方式来实现。

❑直接在页面中添加元素和Angular指令,依赖控制器中构建的属性和方式绑定模板中的元素内容和事件,实现应用需求。

❑通过“type”类型为“text/ng-template”的<script>元素来构建一个用于绑定数据的模板,在模板内部添加数据绑定和元素的事件。

❑通过添加元素的“src”属性,导入一个外部文件作为绑定数据的模板,在导入数据模板时,除添加“src”属性外,还需使用“ng-include”指令。

接下来,通过一个简单的示例来演示构建模板内容的方式。

示例2-9 构建模板内容

(1)功能描述

在页面中,首先,通过<script>元素构建了一个显示两项数据信息的模板;然后,在新增的一个<div>元素中将模板的内容导入到元素中。

(2)实现代码

新建一个HTML文件2-9.html,加入如代码清单2-9所示的代码。

代码清单2-9 构建模板内容

        <!doctype html>
        <html ng-app="a2_9">
        <head>
            <title>构建模板内容</title>
            <script src="../Script/angular.min.js"
                     type="text/javascript"></script>
        </head>
        <body>
            <script type="text/ng-template" id="tplbase">
                姓名:{{ name }},<br />邮箱:{{email}}
            </script>
            <div ng-include src="'tplbase'" ng-controller="c2_9"></div>
            <script type="text/javascript">
                var a2_9 = angular.module('a2_9',[]);
                a2_9.controller('c2_9',['$scope',function ($scope) {
                      $scope.name = '陶国荣';
                      $scope.email = 'tao_guo_rong@163.com';
                }]);
            </script>
        </body>
        </html>

(3)页面效果

执行HTML文件2-9.html,最终实现的页面效果如图2-9所示。

图2-9 构建模板内容

(4)源码分析

在本示例的源码中,先添加一个“type”类型为“text/ng-template”的<script>元素,并在该元素中通过双花括号的方式绑定控制器中需要显示的两项数据。由于该元素定义的是Angular类型的模板,因此,它可以直接使用Angular中的表达式进行数据绑定,除此之外,还可以在模板中绑定元素的各类事件。

在完成模板内容构建之后,新添加一个<div>元素,用来导入模板内容。在导入时,首先添加“ng-include”指令,声明该元素的内容来源于其他模板;然后再添加“src”属性,指定对应模块的名称,即ID号或模板文件名称。需要注意的是:“src”属性值是一个表达式,它可以是$scope中的属性名,也可以是普通字符串。如果是后者,必须添加引号。

2.3.2 使用指令复制元素

在构建模板内容的过程中,有时需要反复将不同的数据加载到一个元素中,例如,通过<li>元素绑定一个数组的各成员。此时,可以使用“ng-repeat”指令,它的功能是根据绑定数组成员的数量,复制页面中被绑定的<li>元素,并在复制过程中添加元素相应的属性和方法,通过这种方式,实现数组数据与元素绑定的过程。

在使用“ng-repeat”指令复制元素的过程中,还提供了几个非常实用的专有变量,可以通过这些变量来处理显示数据时的各种状态。这些变量的功能分别如下。

❑$f irst,该变量表示记录是否是首条,如果是则返回true,否则返回false。

❑$last,该变量表示记录是否是尾条,如果是则返回true,否则返回false。

❑$middle,该变量表示记录是否是中间条,如果是则返回true,否则返回false。

❑$index,该变量表示记录的索引号,其对应的值从0开始。

接下来,通过一个示例来演示使用“ng-repeat”指令复制元素的过程。

示例2-10 使用指令复制元素

(1)功能描述

在页面中,通过<li>元素显示一个数组中的全部成员数据,并且在显示数据时列出当条记录是否为“首条”和“尾条”记录的状态信息。

(2)实现代码

新建一个HTML文件2-10.html,加入如代码清单2-10所示的代码。

代码清单2-1 0使用指令复制元素

    <!doctype html>
    <html ng-app="a2_10">
    <head>
        <title>使用指令复制元素</title>
        <script src="../Script/angular.min.js"
                 type="text/javascript"></script>
        <style type="text/css">
            body {
                  font-size: 12px;
            }
            ul {
                  list-style-type: none;
                  width:400px;
                  margin: 0px;
                  padding: 0px
            }
                  ul li {
                      float: left;
                      padding: 5px 0px;
                  }
                      ul li span {
                            width: 50px;
                            float: left;
                            padding: 0px 10px;
                      }
        </style>
    </head>
    <body>
        <div ng-controller="c2_10">
            <ul>
                  <li>
                      <span>序号</span>
                      <span>姓名</span>
                      <span>性别</span>
                      <span>是否首条</span>
                      <span>是否尾条</span>
                  </li>
                  <li ng-repeat=" stu in data">
                      <span>{{$index+1}}</span>
                      <span>{{stu.name}}</span>
                      <span>{{stu.sex}}</span>
                      <span>{{$first?'是':'否'}}</span>
                      <span>{{$last?'是':'否'}}</span>
                  </li>
            </ul>
        </div>
        <script type="text/javascript">
            var a2_10 = angular.module('a2_10',[]);
            a2_10.controller('c2_10',['$scope',function ($scope) {
                $scope.data = [
                { name: "张明明",sex: "女" },
                { name: "李清思",sex: "女" },
                { name: "刘小华",sex: "男"},
                { name: "陈忠忠",sex: "男" }
                ];
            }]);
        </script>
    </body>
    </html>

(3)页面效果

执行HTML文件2-10.html,最终实现的页面效果如图2-10所示。

图2-10 使用指令复制元素

(4)源码分析

在本示例的源码中,首先,在对应页面的控制器代码中,以$scope属性的方式添加了一个名为“data”的数组,用作页面中<li>元素绑定的数据源;然后,在页面中添加<ul>元素,并在该元素中再添加两个<li>元素,第一个用于标题显示,第二个用于绑定“data”数组内容。

在用于绑定“data”数组内容的第二个<li>元素中,调用Angular中的“ng-repeat”指令完成数据与页面元素的绑定。在绑定过程中,Angular先遍历“data”数组,在遍历时复制一份<li>元素,并通过“stu”对象将控制器中的属性和方法添加至该元素中,在遍历完成后便生成了与数组成员数量对应的<li>元素,并且在这些元素中也添加了需要显示的内容和方法,从而最终实现以列表方式显示集合数据的功能。

在通过“ng-repeat”指令复制元素过程中,可以通过表达式的方式直接调用Angular提供的几个专用的变量$f irst、$middle、$last和$index。由于这些变量均返回布尔值,因此,可以根据返回的布尔值,再借助“?:”运算符转成中文显示的文字内容,实现的过程如本示例中的源码所示。

2.3.3 添加元素样式

在Angular中,添加元素的样式也非常简单,概括起来可以通过下列几种方式来进行。

(1)直接绑定值为CSS类别名称的$scope对象属性

这种方式的操作非常简单,先在控制器中添加一个值为CSS类别名称的属性,然后在页面元素的class或ng-class属性值中,通过双花括号方式绑定属性名即可,如以下代码。

    $scope.red="red";

上述代码表示在控制器中添加了一个名为“red”的属性,它的属性值是名为“red”的CSS类别名。在添加完“red”属性后,在页面中可以通过下列代码进行调用。

    <div ng-class="{{red}}"></div>

等价于下列代码:

    <div class="{{red}}"></div>

这种方式操作起来虽然简单,但在控制器中定义CSS类别名称,并不是Angular所提倡的,我们在开发Angular应用时,尽量保证控制器的代码都是处理业务逻辑,并不涉及页面元素。

(2)以字符串数组方式选择性添加CSS类别名称

这种方式将根据控制器中一个布尔类型的属性值来决定页面中元素添加的CSS样式名,当该属性值为true时,添加一个CSS样式名,当属性值为false值时,添加另外一个CSS样式名。下列代码添加一个名为“blnfocus”属性,由它决定页面中<div>元素的样式。

    $scope.blnfocus=true;

接下来在页面的<div>元素中添加ng-class属性,并在属性值中通过字符串数组方式来添加CSS类别名称,代码如下。

    <div ng-class="{true:'red',false:’green’}[blnfocus]"></div>

在上述代码中,<div>的CSS样式取决于“blnfocus”属性值,当该值为true时,添加“red”样式名,当该值为false时,添加“green”样式名。这种方式只能在两种样式中选择一种,并且{}和[]顺序不可颠倒。在{}中设置可选择的两种样式名称,在[]中放置控制样式的属性名。

(3)通过定义key/value对象的方式来添加多个CSS类别名称

与前面介绍的两种添加CSS类别名的方法相比,第三种方法的功能要强大很多,它可以根据控制显示样式的属性值添加多个样式名。例如,先添加两个用于控制样式显示的“a”和“b”属性,这两个属性的类型均为布尔型,代码如下。

    $scope.a=false;
    $scope.b=true;

接下来在页面的<div>元素中,添加ng-class属性。在设置属性值时,通过定义key/value对象的方式来添加多个CSS样式名,代码如下。

    <div ng-class="{'red':a,'green':b}"></div>

在上述ng-class属性值设置过程中,样式名“red”和“green”分别为“key”值,属性值“a”和“b”分别为对应的“value”值。当“value”属性值为true时,则添加对应的“key”即CSS样式名。因此,这种方式可以实现添加多种CSS样式名称,适合在处理复杂样式时使用。

此外,在Angular中,还有另外两个用于添加样式的页面指令,分别为“ng-class-odd”和“ng-class-even”,这两个样式指令是专用于以列表方式显示数据,对应奇数行与偶数行的样式。

接下来,通过一个完整的示例来详细介绍样式在页面中的使用。

示例2-11 添加元素样式

(1)功能描述

在示例2-10的基础之上,在样式方面增加3个功能。首先,将第一个<li>元素中显示的字体加粗;其次,添加“ng-class-odd”和“ng-class-even”两个指令,实现列表的隔行变色的功能;最后,当单击某行<li>的元素时,显示相应的背景色。

(2)实现代码

新建一个HTML文件2-11.html,加入如代码清单2-11所示的代码。

代码清单2-11 添加元素样式

    <!doctype html>
    <html ng-app="a2_11">
    <head>
        <title>添加元素样式</title>
        <script src="../Script/angular.min.js"
                 type="text/javascript"></script>
        <style type="text/css">
            body {
                  font-size: 12px;
            }
            ul {
                  list-style-type: none;
                  width: 408px;
                  margin: 0px;
                  padding: 0px
              }
                  ul li {
                      float: left;
                      padding: 5px 0px;
                  }
                  ul .odd {
                      color:#0026ff;
                  }
                  ul .even {
                      color:#ff0000;
                  }
                  ul .bold{
                      font-weight:bold;
                  }
                  ul li span {
                      width: 52px;
                      float: left;
                      padding: 0px 10px;
                  }
                  ul .focus {
                      background-color:#cccccc;
                  }
          </style>
    </head>
    <body>
          <div ng-controller="c2_11">
              <ul>
                  <li ng-class="{{bold}}">
                      <span>序号</span>
                      <span>姓名</span>
                      <span>性别</span>
                      <span>是否首条</span>
                      <span>是否尾条</span>
                  </li>
                  <li ng-class-odd="'odd'"
                       ng-class-even="'even'"
                       ng-repeat=" stu in data"
                       ng-click='li_click($index)'
                       ng-class='{focus: $index==focus}'>
                       <span>{{$index+1}}</span>
                       <span>{{stu.name}}</span>
                       <span>{{stu.sex}}</span>
                       <span>{{$first?'是':'否'}}</span>
                       <span>{{$last?'是':'否'}}</span>
                  </li>
              </ul>
          </div>
          <script type="text/javascript">
              var a2_11 = angular.module('a2_11',[]);
              a2_11.controller('c2_11',['$scope',function ($scope) {
                  $scope.bold = "bold";
                  $scope.li_click = function (i) {
                      $scope.focus = i;
                  };
                  $scope.data = [
                  { name: "张明明",sex: "女" },
                  { name: "李清思",sex: "女" },
                  { name: "刘小华",sex: "男" },
                  { name: "陈忠忠",sex: "男" }
                  ];
              }]);
          </script>
      </body>
      </html>

(3)页面效果

执行HTML文件2-11.html,最终实现的页面效果如图2-11所示。

图2-11 添加元素样式

(4)源码分析

在本示例的源代码中,首次定义了名称为“odd”、“even”、“bold”和“focus”的4个样式,分别用于隔行时的两种字体色、标题栏字体变粗和单击某行时的背景色。

其次,在示例的控制器代码中,除添加“data”数组集合外,再添加了一个“bold”属性,用于指定加粗显示字体时的样式名。另外,还添加了一个带参数的“li_click”方法,当调用该方法时,将“focus”属性值设为传入参数值。

再有,在示例的页面代码中,先通过双花括号方式将第一个<li>元素的“ng-class”值与“bold”属性值绑定,由于该值指定的是一个加粗时的样式名,因此,在绑定后,<li>元素中的字体变粗;然后,使用“ng-class-odd”和“ng-class-even”样式指令分别绑定奇数和偶数行的样式名,实现隔行换色的功能;最后,在<li>元素的单击事件中,将执行$scope对象中添加的“li_click()”方法,在该方法中将“$index”(行号值)作为实参传给方法,并且将“focus”属性值设置为“$index”值。因此,当单击某行<li>元素时,“focus”属性值将变为相应的“$index”值。

最后,在示例页面<li>元素的“ng-class”样式指令值中通过key/value对象的方式指定该元素需要添加的样式。由于单击<li>元素时,“$index”变量值和“focus”属性值相同,也就是说,表达式“$index==focus”的返回值为true。因此,“ng-class”样式指令值变为“focus”,最终实现当单击<li>元素时,添加背景样式的页面效果。

2.3.4 控制元素的隐藏与显示状态

在Angular中,可以通过“ng-show”“ng-hide”和“ng-switch”指令来控制元素隐藏与显示的状态,前两个指令直接控制元素的显示和隐藏状态,当“ng-show”值为true或“ng-hide”值为false时,元素显示,反之,元素隐藏。

后一个“ng-switch”指令的功能是显示匹配成功的元素,该指令需要结合“ng-switch-when”和“ng-switch-defalut”指令使用。在“ng-switch”指令中,当指定的“on”值与某个或多个添加“ng-switch-when”指令的元素匹配时,这些元素显示,其他未匹配的元素则隐藏。如果没有找到与“on”值相匹配的元素时,则显示添加了“ng-switch-defalut”指令的元素。

接下来,通过一个示例来演示控制元素隐藏或显示状态的过程。

示例2-12 控制元素的隐藏与显示状态

(1)功能描述

在页面中,调用“ng-show”“ng-hide”和“ng-switch”指令绑定$scope对象的属性值,控制<div>和<li>元素显示或隐藏的状态。

(2)实现代码

新建一个HTML文件2-12.html,加入如代码清单2-12所示的代码。

代码清单2-12 控制元素的隐藏与显示状态

    <!doctype html>
    <html ng-app="a2_12">
    <head>
        <title>控制元素显示和隐藏的状态</title>
        <script src="../Script/angular.min.js"
                 type="text/javascript"></script>
        <style type="text/css">
            body {
                  font-size: 12px;
            }
            ul {
                  list-style-type: none;
                  margin: 0px;
                  padding: 0px
              }
              div {
                  margin: 8px 0px;
              }
          </style>
    </head>
    <body>
          <div ng-controller="c2_12">
               <div ng-show={{isShow}}>陶国荣</div>
               <div ng-hide={{isHide}}>tao_guo_rong@163.com</div>
               <ul ng-switch on={{switch}}>
                  <li ng-switch-when="1">陶国荣</li>
                  <li ng-switch-when="2">tao_guo_rong@163.com</li>
                  <li ng-switch-default>更多...</li>
              </ul>
          </div>
          <script type="text/javascript">
              var a2_12 = angular.module('a2_12',[]);
              a2_12.controller('c2_12',['$scope',function ($scope) {
                  $scope.isShow = true;
                  $scope.isHide = false;
                  $scope.switch = 3;
              }]);
          </script>
    </body>
    </html>

(3)页面效果

执行HTML文件2-12.html,最终实现的页面效果如图2-12所示。

图2-12 控制元素显示和隐藏的状态

(4)源码分析

在本示例的源代码中,前两个<div>元素分别添加了“ng-show”和“ng-hide”,并通过双花括号绑定了“isShow”属性和“isHide”属性,这两个属性在控制器中添加时的值分别为true和false,因此,这两个<div>元素都将显示在页面中。

此外,在添加“ng-switch”指令的<ul>元素中,由于“on”值绑定了“switch”属性,而该属性在控制器中添加时的值为3,并且在页面中添加了“ng-switch-when”指令的<li>元素中,没有找到“ng-switch-when”指令值为3的元素,因此,只能显示添加了“ng-switch-default”指令的<li>元素,即最后一行显示内容为“更多…”的<li>元素,最终显示效果如图2-12所示。

2.4 模板中的表单控件

在介绍完Angular中的模板内容定义的方法之后,接下来介绍模块中一个非常重要的控件——表单控件。表单是各类控件(如input、select、textarea)的集合体,这些控件依附于表单,形成DOM元素中最为重要的数据交互元素,而Angular也对表单中的控件做了专门的包装,其中最重要的一项就是控件的自我验证功能,接下来我们逐一进行介绍。

2.4.1 表单基本验证功能

在Angular中,专门针对表单和表单中的控件提供了下列属性,用于验证控件交互值的状态。

❑$pristine表示表单或控件内容是否未输入过。

❑$dirty表示表单或控件内容是否已输入过。

❑$valid表示表单或控件内容是否已验证通过。

❑$invalid表示表单或控件内容是否未验证通过。

❑$error表示表单或控件内容验证时的错误提示信息。

前面4项属性均返回布尔类型的值,最后一项属性返回一个错误对象,包含全部表单控件验证时的返回的错误信息。接下来,通过一个简单的示例来演示表单控制验证的过程。

示例2-13 表单基本验证功能

(1)功能描述

在页面的表单中,添加两个<input>文本框输入元素,第一个用于输入姓名,要求输入内容不能为空;第二个用于输入邮件地址,除输入内容不为空时,邮件地址的格式必须正确,当表单验证失败时,“提交”按钮将不可用。

(2)实现代码

新建一个HTML文件2-13.html,加入如代码清单2-13所示的代码。

代码清单2-13 表单基本验证功能

    <!doctype html>
    <html ng-app="a2_13">
    <head>
    <title>表单基本验证功能</title>
    <script src="../Script/angular.min.js"
             type="text/javascript"></script>
    <style type="text/css">
        body {
            font-size: 12px;
        }
        input{
            padding:3px;
        }
    </style>
</head>
<body>
    <form name="temp_form"
           ng-submit="save()"
           ng-controller="c2_13">
        <div>
            <input name="t_name" ng-model="name"
                    type="text" required />
            <span ng-show="temp_form.t_name.$error.required">
                  姓名不能为空!
            </span>
        </div>
        <div>
            <input name="t_email" ng-model="email"
                    type="email" required />
            <span ng-show="temp_form.t_email.$error.required">
                  邮件不能为空!
            </span>
            <span ng-show="temp_form.t_email.$error.email">
                  邮件格式不对!
            </span>
        </div>
        <input type="submit"
                ng-disabled="temp_form.$invalid"
                value="提交" />
    </form>
    <script type="text/javascript">
        var a2_13 = angular.module('a2_13',[]);
        a2_13.controller('c2_13',['$scope',function ($scope) {
            $scope.name = "陶国荣";
            $scope.email = "tao_guo_rong@163.com";
            $scope.save = function () {
                  console.log("提交成功!");
            }
        }]);
    </script>
</body>
</html>

(3)页面效果

执行HTML文件2-13.html,最终实现的页面效果如图2-13所示。

图2-13 表单基本验证功能

(4)源码分析

在本示例的源码中,当构建应用控制器代码时,先添加与页面层输入文本框相对应的两个属性“name”和“email”,再添加一个“save”方法,用于单击“提交”按钮时执行。

在页面代码中,为了使用Angular中提供表单控件的验证属性,必须在输入文本框中添加“ng-model”指令并绑定控制器中添加的相应属性名,否则,无法执行Angular的表单控件的验证功能,这点必须注意。

然后,再添加<span>元素,将验证时返回的错误信息状态作为“ng-show”指令值,用于控制<span>元素的显示或隐藏,如果错误信息状态返回true,表示出现错误,那么显示<span>元素中的提示信息,反之,隐藏<span>元素。

最后,在添加“提交”按钮时,将该按钮的不可用性“ng-disabled”指令与表单是否未验证成功的属性值相绑定,即如果返回true,表示表单验证未成功,那么“提交”按钮的不可用性也为true,变为不可用的灰色,否则变为可以单击的可用状态。

2.4.2 表单中的checkbox和radio控件

在表单控件中,checkbox控件和radio控件与<input>元素的其他类型控件不同,这两个控件不具有Angular的控件验证功能,而且checkbox有选中和非选中两种状态,而radio只有一种选中状态。checkbox控件和radio控件都可以通过“ng-model”指令绑定控制器的属性,一旦绑定完成,在首次加载时,将以绑定的属性值作为控件初始化的状态。

接下来,通过一个简单的示例来演示这两个控件操作的过程。

示例2-14 表单中的checkbox和radio控件

(1)功能描述

在页面的表单中,分别添加一个“type”类型为“checkbox”和两个“type”类型为“radio”的<input>元素,并添加一个“提交”按钮,当单击此按钮时,显示这些表单控件所选中的值。

(2)实现代码

新建一个HTML文件2-14.html,加入如代码清单2-14所示的代码。

代码清单2-14 表单中的checkbox和radio控件

    <!doctype html>
    <html ng-app="a2_14">
    <head>
        <title>表单中的checkbox和radio控件</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>
        <form name="temp_form"
               ng-submit="save()"
               ng-controller="c2_14">
            <div>
                  <input type="checkbox"
                          ng-model="a" ng-true-value="同意"
                          ng-false-value="不同意" />
                  同意
            </div>
            <div>
                  性别:
                  <input type="radio" ng-model="b" value="男" />男
                  <input type="radio" ng-model="b" value="女" />女
            </div>
            <input type="submit" value="提交" />
            <div>{{c}}</div>
        </form>
        <script type="text/javascript">
            var a2_14 = angular.module('a2_14',[]);
            a2_14.controller('c2_14',['$scope',function ($scope) {
                  $scope.a = "同意";
                  $scope.b = "男";
                  $scope.save = function () {
                          $scope.c = "您选择的是:" + $scope.a + "和" + $scope.b;
                      }
                  }]);
            </script>
        </body>
        </html>

(3)页面效果

执行HTML文件2-14.html,最终实现的页面效果如图2-14所示。

图2-14 表单中的checkbox和radio控件

(4)源码分析

在本示例的源码中,当构建应用的控制器代码时,先向$scope对象添加“a”和“b”两个属性,分别用于表单中checkbox控件和radio控件的数据绑定。同时,这两个属性的初始值决定控件元素显示的初始化状态。

然后,在页面代码中,当添加“checkbox”类型控件时,除了添加“ng-mode”指令绑定控制器中的属性外,还添加了“ng-true-value”和“ng-false-value”指令,前者表示选中时返回的值,后者表示未选中时返回的值。另外,在添加“radio”类型控件时,只要将多个控件的“ng-mode”指令值设为相同,这些“radio”类型控件就只有一个选中时的值,并且,当一个选中时,其他控件自动变成非选中的状态。

最后,当单击“提交”按钮时,将执行控制器中的“save”方法。在该方法中,将绑定控件后的“a”和“b”属性值赋予新添加的属性“c”,而“c”属性又通过双花括号的方式被绑定在<div>元素中,因此,在单击“提交”按钮后,控件选中的值将显示在<div>元素中。

2.4.3 表单中的select控件

在Angular中,与其他表单中的控件元素相比,select控件的功能要强大很多,它可以借助“ng-options”指令属性,将数组、对象类型的数据添加到<option>元素中,同时还能在添加数据时进行分组显示。select控件绑定数据的形式有下列几种。

(1)绑定简单的数组数据

这种方式是最为常用,也是最为简单,只需要在控制器中添加一个数组,代码如下。

    $scope.data = ['A','B','C','D']

然后,在页面<select>控件中,通过“ng-options”指令属性值,采用“…for…in…”格式将数组与<select>控件绑定,代码如下。

    <select ng-model="a" ng-options="txt for txt in data">
        <option value="">--请选择--</option>
    </select>

在上述页面代码中,必须添加“ng-model”属性,否则无法绑定控制器中的数组,并且“ng-model”的属性值就是<select>类型控件的选中值,它们之间是双向绑定的关系。

(2)绑定简单的对象数据

除绑定简单的数组外,<select>类型控件还可以绑定一个对象,实现过程也非常简单,只要先在控制器中添加一个对象,代码如下。

    $scope.data = [
          { id: '1',name: 'A' },
          { id: '2',name: 'B' },
          { id: '3',name: 'C' },
          { id: '4',name: 'D' }
    ]

然后,在页面<select>类型控件中,通过“ng-options”指令属性值,采用“…as…for…in…”格式将对象与<select>类型控件绑定,代码如下。

    <select ng-model="a"
            ng-options="txt.id as txt.name for txt in data">
            <option value="">--请选择--</option>
    </select>

在上述页面代码中,在设置<select>类型控件的“ng-options”属性值时,“as”前面部分对应元素的value值,用于选中时获取,“as”后面部分对应元素的text值,用于直接显示。

(3)以分组的形式绑定对象数据

除直接绑定对象数据外,还可以将对象中的数据进行分组绑定显示。实现的方式也很简单,只要先在控制器中添加一个有分组成员的对象数据,代码如下。

    $scope.data = [
          { id: '1',name: 'A',key: '大写' },
          { id: '2',name: 'B',key: '大写' },
          { id: '3',name: 'c',key: '小写' },
          { id: '4',name: 'd',key: '小写' }
    ]

在页面的表单中,分别添加两个<select>表单控件。第一个绑定一个普通的对象,第二个绑定一个带分组显示的对象。当选择这两个控件的选项时,分别显示所选中的选项值。

在上述代码中,如果对象“data”中的“key”为分组成员,那么,在页面<select>控件的“ng-options”属性中采用“…as…group by…for…in…”格式,可以实现对象按“key”成员分组绑定并显示的功能,代码如下。

    <select ng-model="a" ng-options="txt.id as txt.name
    group by txt.key for txt in data">
                <option value="">--请选择--</option>
    </select>

通过上面几种<select>类型控件绑定数据的介绍,相信大家对在Angular中使用<select>控件有了一个初步的认识。接下来,再通过一个完整的示例来详细演示<select>类型控件的使用方法。

示例2-15 表单中的select控件

(1)功能描述

(2)实现代码

新建一个HTML文件2-15.html,加入如代码清单2-15所示的代码。

代码清单2-15表单中的select控件

    <!doctype html>
    <html ng-app="a2_15">
    <head>
        <title>表单中的select控件</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>
        <form name="temp_form" ng-controller="c2_15">
            <div>学制:
                  <select ng-model="a"
                           ng-options="v.id as v.name for v in a_data"
                           ng-change="a_change(a)">
                      <option value="">--请选择--</option>
                  </select>
                  <span>{{a_show}}</span>
            </div>
            <div>班级:
                  <select ng-model="b"
                           ng-options="v.id as v.name
    group by v.grade for v in b_data"
                           ng-change="b_change(b)">
                        <option value="">--请选择--</option>
                    </select>
                    <span>{{b_show}}</span>
              </div>
          </form>
          <script type="text/javascript">
              var a2_15 = angular.module('a2_15',[]);
              a2_15.controller('c2_15',['$scope',function ($scope) {
                    $scope.a_data = [
                    { id: "1001",name: "小学" },
                    { id: "1002",name: "初中" },
                    { id: "1003",name: "高中" }
                    ];
                    $scope.b_data = [
                    { id: "1001",name: "(1)班",grade: "一年级" },
                    { id: "1002",name: "(2)班",grade: "一年级" },
                    { id: "2001",name: "(1)班",grade: "二年级" },
                    { id: "2002",name: "(2)班",grade: "二年级" },
                    { id: "3001",name: "(1)班",grade: "三年级" },
                    { id: "3002",name: "(2)班",grade: "三年级" }
                    ];
                    $scope.a = "";
                    $scope.b = "";
                    $scope.a_change = function (a) {
                        $scope.a_show = "您选择的是:" + a;
                    }
                    $scope.b_change = function (b) {
                        $scope.b_show = "您选择的是:" + b;
                    }
              }]);
          </script>
    </body>
    </html>

(3)页面效果

执行HTML文件2-15.html,最终实现的页面效果如图2-15所示。

图2-15 表单中的select控件

(4)源码分析

在构建本示例的控制器代码时,为了向页面中的两个<select>表单控件提供数据源,先分别添加两个数据对象a_data和b_data,并且在b_data数据对象中指定“grade”属性为分组绑定显示的成员,即按该属性名进行分组绑定对象数据。

然后,为了动态获取两个select表单控件选中的值,在控制器中分别添加了两个属性a和b,用于控件的“ng-model”属性值的绑定,当这两个属性与页面中的两个<select>控件绑定之后,在控制器代码中就可以调用这两个属性值来获取控件当前所选中的选项值。

最后,在控制器代码中添加了两个方法“a_change”和“b_change”,分别用于绑定两个<select>表单控件的“ng-change”事件,在触发事件执行绑定方法的过程中,分别将<select>控件选中的值作为新添属性“a_show”和“b_show”的值,这两个属性又与页面中的两个<span>元素进行双向绑定。因此,最终实现将选择的内容值显示在页面中。

在两个<select>控件的“ng-options”属性值中,分别采用“…as…for…in…”和“…as…group by …for…in…”的格式,实现对控制器中a_data数据对象的绑定和b_data数据对象的分组绑定。在分组绑定显示时,“group by”后直接指定分组的对象属性名,本示例为“grader”属性名,在分组绑定对象数据后,最终显示的页面效果,如图2-15所示。

2.5 本章小结

本章首先从最基础的Angular表达式讲起,并由表达式的使用过渡到Angular控制器的构建,通过一个个精心设计的简单示例,详细介绍了控制器的定义和属性、方法的添加。

然后,通过控制器的构建引入了Angular中的模板概念。根据一个个简单实用的示例,由浅入深地带领读者逐一掌握模块内容构建、复制元素、添加样式和控制元素显示状态的方法。

最后,介绍了模块中最为重要的表单控件,从控件的基础验证功能讲起,介绍表单中各类重要控件的完整使用方法。

本章内容旨在使用读者初步掌握构建一个简单的Angular应用的基本步骤,同时,也为下一章的学习打下扎实的实践基础。