指令的做用:实现语义化标签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
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文件中。
先看例子,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结构。
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函数,作法请看下一个例子。
这是《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 }
本身跑一跑例子,研究一番,很少解释。
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单元测试