《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

相關文章
相關標籤/搜索