《AngularJS》5个实例详解Directive(指令)机制

本文整理并扩展了《AngularJS》这本书第六章里面的内容,此书近期即将由电子工业出版社出版,敬请期待口令:Angular

1.一点小说明

指令的做用:实现语义化标签css

咱们经常使用的HTML标签是这样的:html

1 <div>
2     <span>一点点内容</span>
3 </div>

而使用AngularJS的directive(指令)机制,咱们能够实现这样的东西:angularjs

1 <tabpanel>
2     <panel>子面板1</panel>
3     <panel>子面板2</panel>
4 </tabpanel>

不少人可能要惊呼,这货和JSP或者Struts等等框架里面的taglib很像啊!浏览器

呃,说实话,实际上就是这样的,只不过这里是使用JavaScript来实现的。正由于如此,因此不少taglib作不到的功能,使用它就均可以作到,好比访问N层scope里面的对象之类的事情(参见后面第5个例子)。app

2.实例1:从最简单的开始

1 <html ng-app='app'>
2     <body>
3         <hello></hello>
4     </body>
5     <script src="../angular-1.0.3/angular.min.js"></script>
6     <script src="HelloDirect.js"></script>
7 </html>

对于以上代码里面的<hello>标签,浏览器显然是不认识的,它惟一能作的事情就是无视这个标签。那么,为了让浏览器可以认识这个标签,咱们须要使用Angular来定义一个hello指令(本质上说就是本身来把<hello>这种玩意儿替换成浏览器能识别的那些标准HTML标签)。框架

来看这段舒适的JS代码:函数

1 var appModule = angular.module('app', []);
2 appModule.directive('hello', function() {
3     return {
4         restrict: 'E',
5         template: '<div>Hi there</div>',
6         replace: true
7     };
8 });

以上代码大概看两眼就能够了,不要太在乎细节。单元测试

 

而后咱们就能够在浏览器里面看到这样的内容:测试

 



 

 

实际产生的标签结构是这样的:ui

 



 

 

能够看到,<hello>这个东东已经被<div>Hi there</div>这个标签替换掉了,这也是以上JS代码里面replace:true这行配置的做用,代码里面的template配置 项固然就是咱们要的div标签啦,至于restrict:'E'这个配置项的含义,请看下表:

 



 

 

ok,看完上面的表格,对于restrict这个属性相信你已经秒懂了,那么咱们来玩儿点花样吧。若是咱们须要替换的HTML标签很长,显然不能用 拼接字符串的方式来写,这时候咱们能够用templateUrl来替代template,从而能够把模板写到一个独立的HTML文件中。

3.实例2:transclude(变换)

先看例子,JS代码:

1 var appModule = angular.module('app', []);
2     appModule.directive('hello', function() {
3     return {
4         restrict: 'E',
5         template: '<div>Hi there <span ng-transclude></span></div>',
6         transclude: true
7     };
8 });

HTML代码:

 1 <html ng-app='app'>
 2     <head>
 3         <meta http-equiv="content-type" content="text/html; charset=utf-8" />
 4     </head>
 5     <body>
 6         <hello>
 7             <br/>
 8             <span>原始的内容,</span><br/>
 9             <span>还会在这里。</span>
10         </hello>
11         <hello>
12         </hello>
13     </body>
14     <script src="../angular-1.0.3/angular.min.js"></script>
15     <script src="Transclude.js"></script>
16 </html>

运行效果以下:


生成的HTML标签结构以下:


 

 

和第一个例子对比,这个例子的JS和HTML代码都略有不一样,JS代码里面多了一个transclude: true,HTML代码里面在<hello>内部出现了子标签。

按照咱们在第一个例子中的说法,指令的做用是把咱们自定义的语义化标签替换成浏览器可以认识的HTML标签。那好,若是咱们自定义的标签内部出现了子标签,应该如何去处理呢?很显然,transclude就是用来处理这种状况的。

对于当前这个例子,transclude的做用能够简化地理解成:把<hello>标签替换成咱们所编写的HTML模板,可是<hello>标签内部的内容保持不变

很显然,因为咱们没有加replace:true选项,因此<hello>标签还在,没有被替换掉。同时,经过这个例子你还还会发现一 个暗藏的属性,那就是浏览器实际上很是智能,虽然它并不认识<hello>这个标签,可是页面没有出错,它只是默默地把这个标签忽略掉了!怎 么样?是否是碉堡了?

你能够本身在上面的JS代码里面加上replace:true,而后再看生成的HTML结构。

4.实例3:关于compile和link

JS代码:

 1 var appModule = angular.module('app', []);
 2 appModule.directive('hello', function() {
 3     return {
 4         restrict: 'E',
 5         template: '<span>Hi there</span>',
 6         replace: true
 7     };
 8 });
 9 appModule.controller('MyController',function($scope) {
10     $scope.things = [1,2,3,4,5,6];
11 });

HTML代码:

1 <html ng-app='app'>
2     <body ng-controller='MyController'>
3         <div ng-repeat='thing in things'>
4             {{thing}}.<hello></hello>
5         </div>
6     </body>
7     <script src="../angular-1.0.3/angular.min.js"></script>
8     <script src="CompileAndLink.js"></script>
9 </html>

呃,这个例子是用来解释一点点理论的,因此单纯看效果可能看不出个鸟。

如前所述,指令的本质实际上是一个替换过程。好,既然如此,Angular究竟是如何进行替换的呢?嗯嗯,这个过程分2个阶段,也就是本节标题所说的compile(编译)和link(链接)了。

简而言之,compile阶段进行标签解析和变换,link阶段进行数据绑定等操做。这里面更加细节的处理过程请参见《AngularJS》这本书中的解析,这里就不赘述了(呃,其实是由于解释起来很长很麻烦,叔懒得在这儿说了

 )。

那么,知道这件事情有什么用途呢?

比方说,你有一些事件须要绑定到某个元素上,那么你须要提供一个link函数,作法请看下一个例子。

5.实例4:一个复杂一点的例子Expander

这是《AngularJS》这本书里面提供的一个例子,可是书里面没有给出完整的可运行代码,因此这里给出来,你们参考一下。

JS代码:

 1 var expanderModule=angular.module('expanderModule', [])
 2 expanderModule.directive('expander', function() {
 3     return {
 4         restrict : 'EA',
 5         replace : true,
 6         transclude : true,
 7         scope : {
 8             title : '=expanderTitle'
 9         },
10         template : '<div>'
11                  + '<div class="title" ng-click="toggle()">{{title}}</div>'
12                  + '<div class="body" ng-show="showMe" ng-transclude></div>'
13                  + '</div>',
14         link : function(scope, element, attrs) {
15             scope.showMe = false;
16             scope.toggle = function toggle() {
17                 scope.showMe = !scope.showMe;
18             }
19         }
20     }
21 });
22 expanderModule.controller('SomeController',function($scope) {
23     $scope.title = '点击展开';
24     $scope.text = '这里是内部的内容。';
25 });

HTML代码:

 1 <html ng-app='expanderModule'>
 2     <head>
 3         <meta http-equiv="content-type" content="text/html; charset=utf-8" />
 4         <script src="../angular-1.0.3/angular.min.js"></script>
 5         <link rel="stylesheet" type="text/css" href="ExpanderSimple.css"/>
 6     </head>
 7     <body>
 8         <div ng-controller='SomeController'>
 9             <expander class='expander' expander-title='title'>
10                 {{text}}
11             </expander>
12         </div>
13     </body>
14     <script src="ExpanderSimple.js"></script>
15 </html>

CSS代码:

 1 .expander {
 2     border: 1px solid black;
 3     width: 250px;
 4 }
 5 .expander>.title {
 6     background-color: black;
 7     color: white;
 8     padding: .1em .3em;
 9     cursor: pointer;
10 }
11 
12 .expander>.body {
13     padding: .1em .3em;
14 }

运行效果以下:

注意一下JS代码里面的这一段:

1 link : function(scope, element, attrs) {
2     scope.showMe = false;
3     scope.toggle = function toggle() {
4         scope.showMe = !scope.showMe;
5     }
6 }

本身跑一跑例子,研究一番,很少解释。

6.实例5:一个综合的例子

JS代码:

 1 var expModule=angular.module('expanderModule',[])
 2 expModule.directive('accordion', function() {
 3     return {
 4         restrict : 'EA',
 5         replace : true,
 6         transclude : true,
 7         template : '<div ng-transclude></div>',
 8         controller : function() {
 9             var expanders = [];
10             this.gotOpened = function(selectedExpander) {
11                 angular.forEach(expanders, function(expander) {
12                     if (selectedExpander != expander) {
13                         expander.showMe = false;
14                     }
15                 });
16             }
17             this.addExpander = function(expander) {
18                 expanders.push(expander);
19             }
20         }
21     }
22 });
23 expModule.directive('expander', function() {
24     return {
25         restrict : 'EA',
26         replace : true,
27         transclude : true,
28         require : '^?accordion',
29         scope : {
30             title : '=expanderTitle'
31         },
32         template : '<div>'
33                    + '<div class="title" ng-click="toggle()">{{title}}</div>'
34                    + '<div class="body" ng-show="showMe" ng-transclude></div>'
35                    + '</div>',
36         link : function(scope, element, attrs, accordionController) {
37             scope.showMe = false;
38             accordionController.addExpander(scope);
39             scope.toggle = function toggle() {
40                 scope.showMe = !scope.showMe;
41                 accordionController.gotOpened(scope);
42             }
43         }
44     }
45 });
46 expModule.controller("SomeController",function($scope) {
47     $scope.expanders = [{
48         title : 'Click me to expand',
49         text : 'Hi there folks, I am the content that was hidden but is now shown.'
50     }, {
51         title : 'Click this',
52         text : 'I am even better text than you have seen previously'
53     }, {
54         title : 'Test',
55         text : 'test'
56     }];
57 });

HTML代码:

 1 <html ng-app="expanderModule">
 2     <head>
 3         <meta http-equiv="content-type" content="text/html; charset=utf-8" />
 4         <script src="../angular-1.0.3/angular.min.js"></script>
 5         <link rel="stylesheet" type="text/css" href="Accordion.css"/>
 6     </head>
 7     <body ng-controller='SomeController' >
 8         <accordion>
 9             <expander class='expander' ng-repeat='expander in expanders' expander-title='expander.title'>
10                 {{expander.text}}
11             </expander>
12         </accordion>
13     </body>
14     <script src="Accordion.js"></script>
15 </html>

CSS代码:

 1 .expander {
 2     border: 1px solid black;
 3     width: 250px;
 4 }
 5 .expander>.title {
 6     background-color: black;
 7     color: white;
 8     padding: .1em .3em;
 9     cursor: pointer;
10 }
11 
12 .expander>.body {
13     padding: .1em .3em;
14 }

运行效果:


这个例子主要的难点在于如何在子Expander里面访问外层Accordion的scope中的数据,这一点解释起来略复杂,这里就不展开了,详细描述参见《AngularJS》一书 

 

AngularJS官方站点在这里:http://angularjs.org/

[全文完]

其它相关内容:

一、OReilly的《AngularJS》已由电子工业出版社出版

http://damoqiongqiu.iteye.com/blog/1965167

二、对比Angular/jQueryUI/Extjs:没有一个框架是万能的

http://damoqiongqiu.iteye.com/blog/1922004

三、AngularJS表单基础

http://damoqiongqiu.iteye.com/blog/1920191

四、AngularJS Form 进阶:远程校验和自定义输入项

http://damoqiongqiu.iteye.com/blog/1920993

五、AngularJS:在Windows上安装Yeoman

http://damoqiongqiu.iteye.com/blog/1885371

六、使用JsTestDriver实现JavaScript单元测试

http://damoqiongqiu.iteye.com/blog/1924415

相关文章
相关标签/搜索