directive 指令一

什麼是Directive

Directive將一段html,js封裝在一塊兒,造成一個能夠複用的獨立個體,具備特定的功能。angularjs中的指令一般是比較小的組件,它至關因而給咱們提供了一些公共的自定義的DOM元素、class屬性或attr屬性。除此以外,咱們能夠在這個基礎上來操做scope,綁定事件,更改樣式等等。經過Directive咱們能夠封裝不少公共指令,好比分頁、自動補全等多個指令,封裝好後,咱們下次複用只要在html頁面上添加該指令就能夠實現複雜的功能。 
總結一下使用Directive的場景: 
1.抽象一個自定義組件,在其餘地方重用。 
2.使html更具語義化,不須要深刻研究代碼和邏輯就能夠知道頁面實現了哪些功能。 
angular封裝了不少本身的指令,都是ng開頭,好比ng-model,ng-controller,ng-show,ng-click等等,咱們也能夠封裝本身的指令,在當前的節點上乾點特別的事。
javascript

 

例子

html部分:html

<body ng-app="testApp">
    <div ng-controller="testController">
        <input type="text" ng-model="city" placeholder="Enter a city" />
        <my-test ng-model="city" ></my-test>
        <span my-test="exp" ng-model="city"></span>
        <span ng-model="city"></span>
    </div>
</body>

js部分java

var app = angular.module('testApp', [
    'directives',
    'controllers'
]);

// 自定義directive
var myDirective = angular.modeule('directives', []);
myDirective.directive('myTest', function() {
    return {
        restrict: 'EMAC',
        require: '^ngModel',
        scope: {
            ngModel: '='
        },
        template: '<div><h4>Weather for {{ngModel}}</h4</div>'
    };
});

// 定義controller
var myControllers = angular.module('controllers', []);
myControllers.controller('testController', [
    '$scope',
    function($scope) {
        $scope.name = 'this is directive1';
    }
]);

以上是一個自定義myTest這個directive的應用。來看一下定義一個directive能夠用哪些格式和參數:angularjs

var myDirective = angular.module('directives', []);

myDirective.directive('directiveName', function() {
    return {
        template: '<div></div>',
        replace: false,
        transclude: true,
        restrict: 'E',
        scope: {},
        controller: function($scope, $element) {

        },
        complie: function(tElement, tAttrs, transclude) {
            return {
                pre: function preLink(scope, iElement, iAttrs, controller) {

                },
                post: function postLink(scope, iElement, iAttrs, controller) {

                }
            };
        },
        link: function(scope, iElement, iAttrs) {

        }
    };
});

咱們能夠發現directive最關鍵的部分就是經過return來設置參數,那麼return裏能夠設置哪些參數呢,下面具體講一講這些參數及其說明。 
1.name 
當前scope的名稱,通常聲明時使用默認值,不用手動設置此屬性。 
2.priority 
優先級。當有多個directive定義在同一個DOM元素上時,有時要明確他們的執行順序。若是優先級相同,那麼執行順序不肯定(通常狀況下,優先級高的先執行,相同優先級的按照先綁定後執行)。 
3.teminal 
最後一組。若是設置爲true,那麼當前的priority將會成爲最後一組執行的directive。也就是說,比這個directive的priority更低的directive將不會執行,同優先級的依然會執行,但執行順序不肯定。 
4.scope 
(1)true 
表示爲這個directive建立一個新的scope。若是在同一個元素中有多個directive須要新的scope的話,它仍是隻會建立一個scope。 
(2){} 
將會建立一個新的,獨立的scope,這個scope與通常的scope的區別在於它不是經過原型繼承父scope的。這對可複用的組件而言頗有用,能夠有效的防止讀取或者修改父級scope的數據。 
5.controller 
controller容許其餘directive經過指定名稱的require進行共享,也就是說directive之間能夠相互溝通,加強相互之間的行爲。controller默認注入如下本地對象: 
$scope:與當前元素結合的scope 
$element:當前元素 
$attrs:當前元素的屬性對象 
$transclude:一個預先綁定到當前scope的轉置linking function 
6.require 
請求另外的controller,傳入當前directive的linking function中,require須要傳入一個directive controller的名稱,若是沒有對應的controller會拋出一個error。名稱能夠加入如下前綴: 
?: 不要拋出異常,這就使得這個依賴變爲一個可選項 
^: 容許查找父元素的controller 
瀏覽器

7.restrict 
EACM的子集的字符串,限制了directive爲指定的聲明方式。省略的話,directive將牢牢容許經過屬性聲明的方式: 
E:元素 
A:屬性 
C:class名 
M:註釋 
比較經常使用的是EA 
8.template 
若是replace爲true,則將模板內容替換當前的html元素,並將原來元素的屬性,class一併轉移;若是replace爲false,則將模板元素做爲當前元素的子元素。 
9.templateUrl 
與template基本一致,但模板經過指定的url進行加載。因爲模板加載是異步的,全部compilation,linking都會暫停,等加載完畢後再執行。 
10.replace 
設置爲true,那麼模板將會替換當前元素,而不是做爲子元素添加到當前元素中。(爲true時模板必需要有一個根節點) 
11.transclude 
編譯元素的內容,使它可以被directive使用。須要在模板中配合ngTransclude使用。 
12.compile 
13.link
app

 

看一個transclude的例子dom

<body>
    <hello>
        <br/><span>原始的內容,</span><br/>
        <span>還會在這裏。</span>
    </hello>
    <hello>
    </hello>
</body>
var appModule = angular.module('app', []);
appModule.directive('hello', function() {
    return {
        restrict: 'E',
        template: '<div>Hi there <span ng-transclude></span></div>',
        transclude: true
    };
});

能夠發現,這裏的ng-transclude會把hello標籤裏內容讀取出來,若是transclude設置爲false,那麼瀏覽器就不會處理hello這個標籤裏內容。 
注意:若是指令使用了transclude參數,那麼控制器就沒法正常監聽數據模型的變化。建議在連接函數裏使用$watch服務。
異步

 

接下來重點講一下scope 
scope主要是用來隔離做用域的,那麼scope在true,false,{}時值是如何變化的呢,看下面一個例子:
ide

<body>  
<div ng-controller='MainController'>  
        父親:{{name}}<input ng-model="name" />  
        <div my-directive></div>  
  </div>  
</body>  
<script type="text/javascript">  
var app = angular.module('myApp', []);  
app.controller('MainController', function ($scope) {  
           $scope.name = '林炳文';  
});  
app.directive('myDirective', function () {  
            return {  
                restrict: 'EA',  
                scope:false,  
                template: '<div>兒子:{{ name }}<input ng-model="name"/></div>'  
            };  
});  
</script>  

scope:false時,子scope繼承父scope的值,改變父親的值,兒子的值也會改變,反之同樣(繼承不隔離) 
false-276.1kB 
scope:true時,子scope繼承父scope的值,改變父親的值,兒子的值也會改變。可是改變兒子的值,父親的值不變(繼承隔離) 
true-312.1kB 
scope:{}時,沒有繼承父scope,改變任何一方不會改變另外一方的值(不繼承隔離) 
{}-310.9kB
函數

當你想要建立一個可重用的組件時,隔離做用域是一個很好的選擇,這樣能夠防止父做用域被污染。那麼隔離做用域後如何訪問父級做用域呢? 

angular經過綁定策略來訪問父做用域的屬性。 
directive提供了三種方法同隔離以外的地方進行交互: 
(1) @ 
主要經過directive所在的標籤屬性綁定外部字符串值。這種綁定是單向的,即父scope變化,directive中的scope的對應屬性也會變化。可是隔離scope中的綁定變化,父scope是不知道的。

<div ng-controller="myController">  
   <div class="result">  
       <div>父scope:  
           <div>Say:{{name}}<br>改變父scope的name:<input type="text" value="" ng-model="name"/></div>  
       </div>  
       <div>隔離scope:  
           <div isolated-directive name="{{name}}"></div>  
       </div>  
        <div>隔離scope(不使用父scope {{name}}):  
             <div isolated-directive name="name"></div>  
        </div>  
   </div>  
</div>
var app = angular.module('myApp', []);  
app.controller("myController", function ($scope) {  
        $scope.name = "hello world";  
}).directive("isolatedDirective", function () {  
        return {  
            scope: {  
                name: "@"  
            },  
            template: 'Say:{{name}} <br>改變隔離scope的name:<input type="buttom" value="" ng-model="name" class="ng-pristine ng-valid">'  
        };  
    });  

geli-732.9kB 
(2) = 
經過directive的attr屬性在局部scope和父scope的屬性名之間創建雙向綁定 
(3) & 
directive在父scope的上下文中執行一個表達式,此表達式是一個function。directive能夠經過一個父scope中的function,當directive中有什麼動做須要更新到父scope中的時候,能夠在父scope上下文中執行一段代碼或一個函數。

<div  ng-controller="myController">  
    <div>父scope:  
        <div>Say:{{value}}</div>  
    </div>  
    <div>隔離scope:  
        <div isolated-directive action="click()"></div>  
    </div>  
</div>  
var app = angular.module('myApp', []);  
app.controller("myController", function ($scope) {  
    $scope.value = "hello world";  
    $scope.click = function () {  
        $scope.value = Math.random();  
    };  
}).directive("isolatedDirective", function () {  
    return {  
        scope: {  
            action: "&"  
        },  
        template: '<input type="button" value="在directive中執行父scope定義的方法" ng-click="action()"/>'  
        }  
})  

scope-164.8kB 
再看一個scope應用的例子

<body ng-app="testApp">
    <div ng-controller="attrtest">
        <my-attr info="naomi"></my-attr>
    </div>
</body>
myDirectives.directive('myAttr', function() {
    return {
        restrict: 'E',
        scope: {
            customerInfo: '=info'
        },
        template: 'Name: {{customerInfo.name}} Address: {{customerInfo.address}}<br>' +
                  'Name: {{vojta.name}} Address: {{vojta.address}}'
    };
});

myControllers.controller('attrtest',['$scope',
    function($scope) {
        $scope.naomi = {
            name: 'Naomi',
            address: '1600 Amphitheatre'
        };
        $scope.vojta = {
            name: 'Vojta',
            address: '3456 Somewhere Else'
        };
    }
]);

結果: 
Name: Address: 
Name: Vojta Address: 3456 Somewhere Else

 

接下來說一下require

<body>
    <outer-directive>
        <inner-directive></inner-directive>
        <inner-directive2></inner-directive2>
    </outer-directive>
    <script>
        var app = angular.module('myApp', []);
        app.directive('outerDirective', function() {
            return {
                scope: {},
                restrict: 'AE',
                controller: function($scope) {      
                    this.say = function(someDirective) { 
                    console.log('Got:' + someDirective.message);
                    };
                }
            };
        });
        app.directive('innerDirective', function() {
            return {
                scope: {},
                restrict: 'AE',
                require: '^outerDirective',
                link: function(scope, elem, attrs, controllerInstance) {
                    scope.message = "Hi,leifeng";
                    controllerInstance.say(scope);
                }
            };
        });
        app.directive('innerDirective2', function() {
            return {
                scope: {},
                restrict: 'AE',
                require: '^outerDirective',
                link: function(scope, elem, attrs, controllerInstance) {
                    scope.message = "Hi,shushu";
                    controllerInstance.say(scope);
                }
            };
        });

    </script>

</body>

結果:
Got:Hi,leifeng
Got:Hi,shushu
上例中的innerDirective和innerDirective2複用了outerDirective的controller中的方法。這也進一步說明,指令中的controller是用來讓不一樣指令間通訊用的。

require的值是另外一個指令的名稱,但實際上引用的是那個指令的控制器實例。require很是有用,由於不少時候指令之間是要相互配合的,好比說require:'ngModel',那麼當angular初始化它的時候,就會在它所在的元素上尋找一個叫作ng-model的指令,而後取得它的控制器實例。找到時候,就能夠把這個控制器的實例做爲Link函數的第四個參數傳進來。 
關於require:'ngModel'其餘的一些用法,會在directive二講到。

 

再說說controller,link,compile之間的關係

myController.controller('directive2',[
    '$scope',
    function($scope) {
        $scope.number = '1111 ';
    }
]);

myDirective.directive('exampleDirective', function() {
    return {
        restrict: 'E',
        template: '<p>Hello {{number}}!</p>',
        controller: function($scope, $element){
            $scope.number = $scope.number + "22222 ";
        },
        link: function(scope, el, attr) {
            scope.number = scope.number + "33333 ";
        },
        compile: function(element, attributes) {
            return {
                pre: function preLink(scope, element, attributes) {
                    scope.number = scope.number + "44444 ";
                },
                post: function postLink(scope, element, attributes) {
                    scope.number = scope.number + "55555 ";
                }
            };
        }
    }
});
<body ng-app="testApp">
    <div ng-controller="directive2">
        <example-directive></example-directive>
    </div>
</body>

看一下結果: 
Hello 1111 22222 44444 5555 ! 
從結果能夠看出,controller先運行,compile後運行,link不運行。 
注掉compile,結果是: 
Hello 1111 22222 33333 ! 
此次是controller先運行,link後運行。 
也就是說,link和compile不兼容,通常的,compile比link的優先級要高。

注意,在controller和link中均可以定義方法,它們的區別是,controller主要是用來提供可在指令間複用的行爲,但link連接函數只能在當前內部指令中定義行爲,沒法再指令複用。

相關文章
相關標籤/搜索