歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> AngularJS指令學習進階

AngularJS指令學習進階

日期:2017/3/1 9:13:07   编辑:Linux編程

緊接上篇“AngularJS 初步認識”http://www.linuxidc.com/Linux/2016-08/134158.htm

一、前言

在AngularJS中指令尤為重要且內容龐多,固單獨提煉出來,梳理一番。如有錯誤,請不吝講解。

好了,言歸正傳,讓我們一起走進Angular指令的世界。

在上篇文章的前言部分提到,Angular的核心就是對HTML標簽的增強。我們用到的諸如ng-app、ng-controller等等這些都是屬於Angular指令,具體點,它們為Angular內置的指令。

Angular不僅提供了內置指令,它還允許我們自定義指令,不然Angular就太low咯。

這也是本篇文章的核心:如何自定義指令。

二、自定義指令

Angular為我們提供了.directive()這個方法,來定義指令。

如下:

正如上述代碼所示,directive方法接受兩個參數:name和factory_function。

  --name嘛,即為指令的名字,供我們調用時使用;

  --factory_function就是當我們調用指令時,指令的實際行為,並且factory_function通常返回一個對象,裡面通過規定的設置項來定義指令。

那麼自定義指令中,我們都可以操作哪些設置項呢?

如下:

var app = angular.module('myApp', []);
app.directive('myDirective', function(){
            
    return {
        restrict: 'EACM',//告訴AngularJS這個指令在DOM中以何種形式被聲明,默認為A
        priority: Number,//優先級參數(值為數值),數值越大,優先級越高,默認為0
        terminal: Boolean,//true或者false,告訴AngularJS是(true)否(false)停止運行當前元素上比本指令優先級低的指令
        template: String or Template Function,//模板
        templateUrl: String,//模板URL
        replace: Boolean,//值為true,代表模板會替換掉調用該指令的元素
        scope: Boolean or Object,//指令作用域
        transclude: Boolean,//true,結合ng-transclude,在該指令中嵌入內容
        controller: String or function($scope, $element, $attrs, $transclude, otherInjectables){...},//指令控制器
        controllerAs: String,//用來設置控制器的別名
        require: String,//將控制器注入到其值所指定的指令中
        link: function(scope, element, attrs){...},
        compile: function(){}               
    };
});
代碼稍長,請自行打開

針對如此眾多的設置項,我們一同先挑幾個常用且簡單的設置項,感受感受吧。

--template & templateUrl--

template模板參數是可選的,其值可以是一段HTML文本,也可以是一個接受兩個參數的函數。

注:如果template值為空,或者沒有template設置項,那麼不會覆蓋嵌入在指令內部的元素,且當指令scope為false或者{…}時,內部元素的作用域共享外部父作用域;但,當指令scope為true時,內部元素的作用域就是繼承自外部父作用域。

demo如下:

<!DOCTYPE html>
    <head>
        <meta charset="utf-8"/>
    </head>
    <body ng-app="myModule">
        outterDirective:<input type="text" ng-model="name"/><br>
        <one>
            innerDirective: <input type="text" ng-model="name"/>
        </one>
        <script src="angular.js"></script>
        <script>
            var myModule = angular.module('myModule', []);
            myModule.directive('one', function(){
                return {
                    scope: false,
                    restrict: 'E'
                }
            });
        </script>
    </body>
</html>
scope為false

效果圖如下:

<!DOCTYPE html>
    <head>
        <meta charset="utf-8"/>
    </head>
    <body ng-app="myModule">
        outterDirective:<input type="text" ng-model="name"/><br>
        <one>
            innerDirective: <input type="text" ng-model="name"/>
        </one>
        <script src="angular.js"></script>
        <script>
            var myModule = angular.module('myModule', []);
            myModule.directive('one', function(){
                return {
                    scope: {},
                    restrict: 'E'
                }
            });
        </script>
    </body>
</html>
scope為{...}

scope:{...}效果同上

<!DOCTYPE html>
    <head>
        <meta charset="utf-8"/>
    </head>
    <body ng-app="myModule">
        outterDirective:<input type="text" ng-model="name"/><br>
        <one>
            innerDirective: <input type="text" ng-model="name"/>
        </one>
        <script src="angular.js"></script>
        <script>
            var myModule = angular.module('myModule', []);
            myModule.directive('one', function(){
                return {
                    scope: true,
                    restrict: 'E'
                }
            });
        </script>
    </body>
</html>
scope為true

scope為true,效果圖如下:

假設,設置了template後,當我們調用指令後,它會呈現在頁面中。下面我們就來具體看看。

1、當template值為一段HTML文本時,demo如下:

<!DOCTYPE html>
    <head>
        <meta charset="utf-8"/>
    </head>
    <body ng-app="myApp">
        <div my-directive></div>
        <script src="angular.js"></script>
        <script>
            var app = angular.module('myApp', []);
            app.directive('myDirective', function(){
                return {
                    template: '<div>\
                                   <h1>Hi everyone!</h1>\
                               </div>'
                };
            });
        </script>
    </body>
</html>
EntireCode

2、當template值為函數時,demo如下:

<!DOCTYPE html>
    <head>
        <meta charset="utf-8"/>
    </head>
    <body ng-app="myApp">
        <div my-directive title="Monkey"></div>
        <script src="angular.js"></script>
        <script>
            var app = angular.module('myApp', []);
            app.directive('myDirective', function(){
                return {
                    /*
                        值為函數時,接受兩個參數tElement, tAttrs:
                            tElement:使用此指令的元素
                            tAttrs:實例的屬性,它是一個由元素上所有的屬性組成的集合,即對象
                    */
                    template: function(tElement, tAttrs){
                        var _html = '';
                        _html +='<div>' + 'Hi,'+tAttrs.title+'</div>';
                        return _html;
                    }
                };
            });
        </script>
    </body>
</html>
代碼稍長,請自行打開

利用template可以在指令中自定義我們需要的模板,但是隨著模板的體積(代碼量)不斷變大時,在指令中利用template就顯得很糟糕,故而,使用templateUrl引用外部模板,是更好的選擇。

--restrict--

restrict 是一個可選參數,它告訴AngularJS這個指令在DOM中以何種形式被調用。一共有四個選擇且都為大寫,不然沒效果,默認為A(屬性)。

A(屬性):

<div my-directive></div>

E(元素):

<my-directive></my-directive>

C(類名):

<div class="my-directive"></div>

M(注釋):

//以注釋調用指令時,前後得有空格
<!-- directive:my-directive -->

這四個選項,可以單獨使用,亦可混合使用。如:

restrict: ‘EA’就代表可以用元素或者屬性調用指令

--replace--

replace是一個可選參數,值為Boolean類型,默認為false,代表模板會被當作子元素插入到調用此指令的元素內部。

如下:

<!DOCTYPE html>
    <head>
        <meta charset="utf-8"/>
    </head>
    <body ng-app="myApp">
        <div my-directive></div>
        <script src="angular.js"></script>
        <script>
            var app = angular.module('myApp', []);
            app.directive('myDirective', function(){
                return {
                    replace: false,
                    template: '<div>Monkey</div>'
                };
            });
        </script>
    </body>
</html>
代碼稍長,請自行打開

運行後,打開chrome調試器,結果如下:

--replace值為true,即模板會替換掉調用指令的元素,如下:

<!DOCTYPE html>
    <head>
        <meta charset="utf-8"/>
    </head>
    <body ng-app="myApp">
        <div my-directive></div>
        <script src="angular.js"></script>
        <script>
            var app = angular.module('myApp', []);
            app.directive('myDirective', function(){
                return {
                    replace: true,
                    template: '<div>Monkey</div>'
                };
            });
        </script>
    </body>
</html>
代碼稍長,請自行打開

運行後,打開chrome調試器,結果如下:

且,當值為true時,template會替代調用指令的元素,固template中的值,必須只包含一個根節點,不然會報錯,如下:

<!DOCTYPE html>
    <head>
        <meta charset="utf-8"/>
    </head>
    <body ng-app="myApp">
        <div my-directive></div>
        <script src="angular.js"></script>
        <script>
            var app = angular.module('myApp', []);
            app.directive('myDirective', function(){
                return {
                    replace: true,
                    template: '<div>Monkey</div><h1>Dorie</h1>'
                };
            });
        </script>
    </body>
</html>
代碼稍長,請自行打開

運行後,打開chrome調試器,報錯如下:

--transclude--

transclude是一個可選參數,表示是否允許在指令中,嵌入內容。值為Boolean,默認值為false代表不允許在指令中嵌入內容,如果手動設置為true後,要結合ng-transclude指令使用,不然和false效果一樣。

下面我們一起來編寫兩個demo具體看看。

<!DOCTYPE html>
    <head>
        <meta charset="utf-8"/>
    </head>
    <body ng-app="myApp">
        <div my-directive>
            <span>需要嵌入內容</span>
        </div>
        <script src="angular.js"></script>
        <script>
            var app = angular.module('myApp', []);
            app.directive('myDirective', function(){
                return {
                    transclude: false,
                    template: '<div>指令內容</div>'
                };
            });
        </script>
    </body>
</html>
transclude為false

運行代碼,打開chrome調試器得下:

我們再來看看transclude:true的情況,將上述代碼中的transclude的值改為true後,運行代碼。

咦,我靠和上訴transclude:false的結果一樣呢?

哦,對了,雖然我們將transclude的值設為true了,但是要結合ng-transclude指令嘛,不然,你讓人家嵌入內容往哪插呢?

利用ng-transclude指令修改代碼後,得下:

<!DOCTYPE html>
    <head>
        <meta charset="utf-8"/>
    </head>
    <body ng-app="myApp">
        <div my-directive>
            <span>需要嵌入內容</span>
        </div>
        <script src="angular.js"></script>
        <script>
            var app = angular.module('myApp', []);
            app.directive('myDirective', function(){
                return {
                    transclude: true,
                    template: '<div>指令內容</div><div ng-transclude></div>'
                };
            });
        </script>
    </body>
</html>
代碼稍長,請自行打開

運行代碼,打開chrome調試器,得下:

注:利用ng-transclude指令標記的元素中,不管有沒有內容,都將等同於無內容情況。

咦,利用transclude嵌入的內容,它們有自己的作用域嗎?如果有,那麼它們的作用域繼承誰呢,指令or外部作用域?

答案:利用ng-transclude嵌入的內容的同時,會創建一個屬於自己的作用域,即子作用域,且繼承自外部作用域,並非指令。

--scope--

scope參數是可選的,它是直接影響指令的作用域。該參數可以被設置為不同的三個值,分別代表三種不同的作用域,下面我們就來一一說明。

1、scope: false

當scope的值為false時,也就是默認狀態,代表指令共享父作用域,即沒有自己的作用域。

如下:

<!DOCTYPE html>
    <head>
        <meta charset="utf-8"/>
    </head>
    <body ng-app="myApp">
        Parent:<input type="text" ng-model="name"/>
        <directive-for-false></directive-for-false>
        <script src="angular.js"></script>
        <script>
            var app = angular.module('myApp', []);
            app.directive('directiveForFalse', function(){
                return {
                    restrict: 'E',
                    scope: false,
                    template: '<div>Child: \
                                  <input type="text" ng-model="name">\
                               </div>'
                };
            });
        </script>
    </body>
</html>
代碼稍長,請自行打開

執行上述代碼,效果圖如下:

2、scope: true

當scope的值為true時,代表指令會創建自己的作用域,且繼承自父作用域。

如下:

<!DOCTYPE html>
    <head>
        <meta charset="utf-8"/>
    </head>
    <body ng-app="myApp">
        Parent:<input type="text" ng-model="name"/>
        <directive-for-true></directive-for-true>
        <script src="angular.js"></script>
        <script>
            var app = angular.module('myApp', []);
            app.directive('directiveForTrue', function(){
                return {
                    restrict: 'E',
                    scope: true,
                    template: '<div>Child: \
                                  <input type="text" ng-model="name">\
                               </div>'
                };
            });
        </script>
    </body>
</html>
代碼稍長,請自行打開

執行上述代碼,效果圖如下:

3、scope: {…}

當scope的值為對象時,代表指令會切斷與外界的聯系,創建一個隔離的作用域,既不繼承於父作用域,也不准內嵌的指令繼承於自己。

一起寫個demo,感受下“隔離作用域”不繼承於父作用域,如下:

<!DOCTYPE html>
    <head>
        <meta charset="utf-8"/>
    </head>
    <body ng-app="myApp">
        Parent:<input type="text" ng-model="name"/>
        <directive-for-obj></directive-for-obj>
        <script src="angular.js"></script>
        <script>
            var app = angular.module('myApp', []);
            app.directive('directiveForObj', function(){
                return {
                    restrict: 'E',
                    scope: {},
                    template: '<div>Child: \
                                  <input type="text" ng-model="name">\
                               </div>'
                };
            });
        </script>
    </body>
</html>

執行上述代碼,效果圖如下:

至於“不准內嵌的指令繼承於自己”,參考上面的--transclude--

那當我們將scope設置為對象後,就成了一個無人問津的作用域,這樣非常適合我們寫插件之類的,但,怎麼與外界交互呢?

這就涉及到所謂的綁定策略啦。

如下:

scope的綁定策略

@

把當前屬性作為字符串傳遞。你還可以綁定來自外層scope的值,在屬性值中插入{{ }}即可。

=

與父scope中的屬性進行雙向綁定。

&

傳遞一個來自父scope的函數

下面,我們就針對上面的提示,逐個demo,感受體會下。

1、@策略

<!DOCTYPE html>
    <head>
        <meta charset="utf-8"/>
    </head>
    <body ng-app="myApp">
    
        <input type="text" ng-model="name"/>
        
        <directive-for-obj who="{{name}}" love="love Dorie"></directive-for-obj>
        <script src="angular.js"></script>
        <script>
            var app = angular.module('myApp', []);
            app.directive('directiveForObj', function(){
                return {
                    restrict: 'E',
                    scope: {
                        who:'@',
                        what: '@love'
                    },
                    template: '<div><input type="text" ng-model="who"/> {{what}}</div>'
                };
            });
        </script>
    </body>
</html>
代碼稍長,請自行打開

效果圖,如下:

2、=策略

<!DOCTYPE html>
    <head>
        <meta charset="utf-8"/>
    </head>
    <body ng-app="myApp">
    
        <input type="text" ng-model="name"/>
        
        <directive-for-obj who="name" love="love Dorie"></directive-for-obj>
        <script src="angular.js"></script>
        <script>
            var app = angular.module('myApp', []);
            app.directive('directiveForObj', function(){
                return {
                    restrict: 'E',
                    scope: {
                        who:'=',
                        what: '@love'
                    },
                    template: '<div><input type="text" ng-model="who"/> {{what}}</div>'
                };
            });
        </script>
    </body>
</html>
代碼稍長,請自行打開

效果圖,如下:

3、&策略

<!DOCTYPE html>
    <head>
        <meta charset="utf-8"/>
    </head>
    <body ng-app="myApp">
    
        <input type="text" ng-model="name"/>
        
        <directive-for-obj who="name" love="love Dorie" greet="sayWhat(wt, whr)"></directive-for-obj>
        <script src="angular.js"></script>
        <script>
            var app = angular.module('myApp', []);
            app.run(function($rootScope){
                $rootScope.sayWhat = function(what, where){
                    alert('hello, '+what+' at '+where);
                }
            });
            app.directive('directiveForObj', function(){
                return {
                    restrict: 'E',
                    scope: {
                        who:'=',
                        what: '@love',
                        greet: '&'
                    },
                    template: '<div><input type="text" ng-model="who"/> {{what}}</div>\
                                <button ng-click="greet({wt: who, whr: who})">click</button>'
                };
            });
        </script>
    </body>
</html>
EntireCode

--controller--

controller參數可以是一個字符串或一個函數。

注:當設置為字符串時,會以該字符串的值為名字,到注冊在應用中的控制器中查找,不管該指令的scope值是什麼,如果應用中的控制器沒有對應的控制器,則報錯。

在控制器中,是的,控制器,包括了指令以及注冊在應用中的,可以注入一些特殊的服務,供其使用,如下:

$scope

與指令元素相關聯的當前作用域

$element

當前指令對應的元素

$attrs

當前元素的屬性組成的對象,如:

<div id=”nav” name=”Monkey”></div>

屬性對象為:

{

id: ‘nav’,

name: ‘Monkey’

}

並且,指令之間的控制器(controller)可以復用。

怎麼復用呢?

這就需要結合下面將要介紹的require設置項啦。

--require--

require參數可以被設置為字符串或數組,字符串代表另外一個指令的名字。Require會將控制器注入到其值所指定的指令中,並作為當前指令的鏈接函數(link)的第四個參數。用法如下:

沒有前綴

指令會在自身提供的控制器中進行查找,如果找不到任何控制器,則會拋出一個error

?

如果在當前的指令沒有找到所需的控制器,則會將null傳給link連接函數的第四個參數

^

如果在當前的指令沒有找到所需的控制器,則會查找父元素的控制器

?^

綜合前面?和^

一些AngularJS相關文章鏈接

AngularJS權威教程 清晰PDF版 http://www.linuxidc.com/Linux/2015-01/111429.htm

希望你喜歡,並分享我的工作~帶你走近AngularJS系列

  1. 帶你走近AngularJS - 基本功能介紹 http://www.linuxidc.com/Linux/2014-05/102140.htm
  2. 帶你走近AngularJS - 體驗指令實例 http://www.linuxidc.com/Linux/2014-05/102141.htm
  3. 帶你走近AngularJS - 創建自定義指令 http://www.linuxidc.com/Linux/2014-05/102142.htm

如何在 AngularJS 中對控制器進行單元測試 http://www.linuxidc.com/Linux/2013-12/94166.htm

在 AngularJS 應用中通過 JSON 文件來設置狀態 http://www.linuxidc.com/Linux/2014-07/104083.htm

AngularJS 之 Factory vs Service vs Provider http://www.linuxidc.com/Linux/2014-05/101475.htm

AngularJS —— 使用 ngResource、RESTful APIs 和 Spring MVC 框架提交數據 http://www.linuxidc.com/Linux/2014-07/104402.htm

AngularJS 的詳細介紹:請點這裡
AngularJS 的下載地址:請點這裡

Copyright © Linux教程網 All Rights Reserved