3.4 指令和过滤器
AngularJS的指令Directive组件与过滤器Filter组件的主要使用场景都是在HTML页面视图里。这两种组件与其他AngularJS组件一样,都是在代码模块Module内定义,只不过它们的实例可以通过AngularJS框架在解析HTML页面视图时自动创建。因为它们都对作用域对象在页面展现有影响,本节将介绍它们的一些基本知识。
3.4.1 指令Directive是什么
在3.3.2节,我们已经提前接触体验过AngularJS框架内置的指令(Directive)了。AngularJS建立了一套完整、可扩展、用来帮助Web应用开发的指令集机制,它使得HTML可以转变成“特定领域语言(DSL)”,是用来扩展浏览器能力的技术之一。在DOM编译期间,和HTML关联着的指令会被AngularJS框架的编译器检测到,并且被调用执行。这种机制使得指令可以为DOM指定扩展行为,或者改变HTML原有组件的默认行为。
指令的实质是“当关联的HTML结构进入编译阶段时应该执行的操作”,它只是一个当编译器编译到相关DOM时需要执行的函数,可以被写在HTML元素的名称,属性,CSS类名和注释里(后两种形式的指令出现和应用较稀少)。
AngularJS通过称为指令属性来扩展的HTML控件属性,其属性名称带有前缀ng-,它的实质是绑定在DOM元素上的函数。在该函数内部可以操作DOM、调用方法、定义行为、绑定控制器及$scope对象等。当浏览器启动、开始解析HTML时,DOM元素上的指令属性就会跟其他属性一样被解析,也就是说当一个AngularJS应用启动,AngularJS编译器就会遍历DOM树来解析HTML,寻找这些指令属性函数,在一个DOM元素上找到一个或多个这样的指令属性函数,它们就会被收集起来、排序,然后按照优先级顺序被执行。AngularJS应用的动态性和响应能力,都要归功于指令属性。比较常见的有在示例3-3中出现过的:ng-app、ng-controller、ng-model,此外其他的如ng-show/ng-hide、ng-class、ng-repeat等多个指令。本书将在它们第一次出现的位置结合示例代码解释其作用。
3.4.2 自定义指令及使用
指令是AngularJS框架里最复杂的组件种类,没有之一。因为指令是AngularJS里被唯一能操作DOM的组件,并且构成了图3.1中数据模型与视图交流互动的桥梁。出于复用和定制的目的,做AngularJS应用开发将不可避免要碰到定义自己的指令的需要。
根据AngularJS官方网站的API参考文档和作者的经验,定义一个指令重要参数的列表如示例3-4所示。
【示例3-4】定义一个AngularJS指令可使用的最重要的参数列表。
//定义名为' myDirectives '的模块 angular.module('myDirectives', []) //定义名为'myDirective001'的指令 .directive('myDirective001', function() { return { restrict: String, template: String or Template Function: function(tElement, tAttrs) (...}, templateUrl: String, replace: Boolean or String, scope: Boolean or Object, controller: String or function(scope, element, attrs, transclude,otherInjectables) { ... }, link: function(scope, iElement, iAttrs, controller) { ... }, …….//此处代表其他被省略的可选参数列表 }; });
【代码解析】代码里先是遵照3.2.3节建议的AngularJS模块划分方案建立了名为myDirectives的模块用于包含所有的自定义指令,随后定义了名为myDirective001的指令。由于定义一个指令可用的参数太多,而有些参数又并不是特别重要或常用,因此笔者将选择关键的一些来说明,其他被本书忽略的完整参数列表读者可以自行查看AngularJS的官方文档说明(https://docs.angularjs.org/api/ng/service/$compile#directive-definition-object):
● restrict(字符串)告诉AngularJS这个指令在DOM中可以何种形式被声明,可选值为E(元素)A(属性)C(CSS类名)M(注释)的任意组合,如'EA'表示该指令可以为HTML元素形式或是某HTML元素的属性形式,这也是被推荐使用的两种形式。
● template(字符串或函数)可以被设置为一段HTML文本(字符串)模板或是一个可以接受两个参数的函数,参数为tElement和tAttrs,并返回一个代表模板的字符串。
● templateUrl(字符串或函数)可以被设置为一个代表外部HTML模板文件路径的字符串或是一个可以接受两个参数的函数,参数为tElement和tAttrs,并返回一个外部HTML模板文件路径的字符串。
● replace(布尔型)默认值为false,表示模板会被当作子元素插入到调用此指令的元素内部,否则模板内容会直接替换掉调用此指令的元素。
● scope(布尔型或对象)参数是可选的,默认值是false,表示该指令没有自己的作用域对象;当它被设置为true时,指令会从父作用域继承并创建一个新的作用域对象;而当它被设置为对象时,指令会创建一个包含该对象的独立作用域对象,这里所谓的独立,是指通过template或templateUrl获得的模板里将无法访问外部作用域对象了。
● controller(字符串或函数)当设置为字符串时,会以字符串的值为名字来查找注册在应用中的控制器的构造函数;而当设置为函数时,将在指令内部通过调用这个匿名构造函数的方式来定义一个内联的控制器对象。
● link(函数)。如果需要在指令中操作DOM,我们需要定义link属性。其中AngularJS传入的参数scope代表指令对应的scope对象,如果指令没有定义自己的本地作用域,那么传入的就是外部的作用域对象;iElement参数代表指令所在DOM对象的jqLite封装。如果使用了template属性,那么iElement对应变换后的DOM对象的jqLite封装;iAttrs参数代表指令所在DOM对象的属性集。这是一个Hash对象,每个键是驼峰规范化后的属性名;controller参数代表指令所在DOM对象对应的控制器对象实例。
学习了指令的定义,就可以写一个最简单的指令来练手了。
【示例3-5】定义一个最简单的AngularJS指令hello。
//定义名为' myDirectives '的模块,并返回模块实例 angular.module('myDirectives', []) //定义名为'hello'的指令 .directive('hello', function() { return { //限制为元素型指令 restrict: 'E', //指定模板内容 template: '<div>Hello Ionic! </div>', //指令内容将替换原内容 replace: true }; });
【代码解析】被定义的指令名为'hello',是元素型的指令。在HTML模板中的该hello元素的内容将被模板字符串代替,即页面里的“<hello>这里可以填任意值</hello>”将被替换为“<div>Hello Ionic! </div>”,然后在DOM中渲染出来。这个例子过于简单,稍微复杂一些的例子读者可以参考本书3.6节中项目演示中的名为stockCode的指令组件。
3.4.3 使用过滤器Filter
过滤器用来格式化需要展示给用户的数据。AngularJS有很多实用的内置过滤器,同时也提供了方便的途径可以自己创建过滤器。常用的AngularJS内置过滤器可参见表3.1。
表3.1 AngularJS内置过滤器及其说明
在HTML模板中使用过滤器的语法与Unix Shell的管道操作命令很相似,如:
{{ 12345.6789 | number }}
的输出显示为12,345.679,
{{ 12345.6789 | number:0 }}
的输出显示为12,346,
{{ 12345.6789 | number:2 }}
的输出显示为12,345.68。
由于AngularJS官方网站(https://docs.angularjs.org/api/ng/filter)已有内置过滤器的详细说明和在线使用示例,本书就不一一介绍了。
与控制器和指令类似,过滤器也是一种AngularJS组件,创建一个过滤器的形式也与创建一个控制器类似。
【示例3-6】定义一个字符串(如一个句子)的首字母转换成大写形式的自定义过滤器。
angular.module('myApp', []) //在myApp模块内动态构建名为capitalize的自定义过滤器组件 .filter('capitalize', function() { // input表示传入的数据 return function(input) { //要求传入需要过滤器处理的值是非空字符串 if(input && angular.isString(input)) { return input[0].toUpperCase() + input.slice(1); } } });
【代码解析】被定义的自定义过滤器组件名为'capitalize',在获得一个字符串后,它将返回把原字符串首字母转换成大写形式的结果字符串。从代码里我们可以看到自定义过滤器的代码格式要求:
● 必须使用模块的filter()接口注册服务。
● 必须提供对象工厂方法。
● 对象工厂必须返回一个过滤器函数,其第一个参数为输入变量。
随后就可以在页面里使用这个capitalize过滤器了,形式如:
{{ 'ionic is so cool! ! ! ' | capitalize}}
的输出显示为Ionic is so cool! ! !。注意原ionic的首字母已经变成大写形式了。