AngularJS Directive用法說(shuō)明

2018-06-07 18:27 更新

Directive可能是AngularJS中比較復(fù)雜的一個(gè)東西了。一般我們將其理解成指令。AngularJS自帶了不少預(yù)設(shè)的指令,比如ng-app,ng-controller這些。可以發(fā)現(xiàn)個(gè)特點(diǎn),AngularJS自帶的指令都是由ng-打頭的。

那么,Directive究竟是個(gè)怎么樣的一個(gè)東西呢?我個(gè)人的理解是這樣的:將一段html、js封裝在一起,形成一個(gè)可復(fù)用的獨(dú)立個(gè)體,具體特定的功能。下面我們來(lái)詳細(xì)解讀一下Directive的一般性用法。

AnguarJS directive的常用定義格式以及參數(shù)說(shuō)明

直接return

看下面的代碼:


var myDirective = angular.module('directives', []);
myDirective.directive('directiveName', function($inject) {
    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) {
        }
    };
});

這里直接return了一個(gè)object,對(duì)象中包括比較多的屬性,這些屬性都是對(duì)自定義directive的定義。詳細(xì)的含義,下面會(huì)繼續(xù)說(shuō)明。

return對(duì)象參數(shù)說(shuō)明


return {
    name: '',
    priority: 0,
    terminal: true,
    scope: {},
    controller: fn,
    require: fn,
    restrict: '',
    template: '',
    templateUrl: '',
    replace: '',
    transclude: true,
    compile: fn,
    link: fn
}

如上所示,return的對(duì)象中會(huì)有很多的屬性,這行屬性都是用來(lái)定義directive的。下面我們來(lái)一個(gè)個(gè)的說(shuō)明他們的作用。

  • name
    表示當(dāng)前scope的名稱,一般聲明時(shí)使用默認(rèn)值,不用手動(dòng)設(shè)置此屬性。
  • priority
    優(yōu)先級(jí)。當(dāng)有多個(gè)directive定義在同一個(gè)DOM元素上時(shí),有時(shí)需要明確他們的執(zhí)行順序。這個(gè)屬性用于在directive的compile function調(diào)用之前進(jìn)行排序。如果優(yōu)先級(jí)相同,則執(zhí)行順序是不確定的(根據(jù)經(jīng)驗(yàn),優(yōu)先級(jí)高的先執(zhí)行,相同優(yōu)先級(jí)時(shí)按照先綁定后執(zhí)行)。
  • teminal
    最后一組。如果設(shè)置為true,則表示當(dāng)前的priority將會(huì)成為最后一組執(zhí)行的directive,即比此directive的priority更低的directive將不會(huì)執(zhí)行。同優(yōu)先級(jí)依然會(huì)執(zhí)行,但是順序不確定。
  • scope
    • true
      將為這個(gè)directive創(chuàng)建一個(gè)新的scope。如果在同一個(gè)元素中有多個(gè)directive需要新的scope的話,它還是只會(huì)創(chuàng)建一個(gè)scope。新的作用域規(guī)則不適用于根模版,因?yàn)楦0嫱鶗?huì)獲得一個(gè)新的scope。
    • {}
      將創(chuàng)建一個(gè)新的、獨(dú)立的scope,此scope與一般的scope的區(qū)別在于它不是通過(guò)原型繼承于父scope的。這對(duì)于創(chuàng)建可復(fù)用的組件是很有幫助的,可以有效的防止讀取或者修改父級(jí)scope的數(shù)據(jù)。這個(gè)獨(dú)立的scope會(huì)創(chuàng)建一個(gè)擁有一組來(lái)源于父scope的本地scope屬性hash集合。這些本地scope屬性對(duì)于模版創(chuàng)建值的別名很有幫助。本地的定義是對(duì)其來(lái)源的一組本地scope property的hash映射。
  • controller
    controller構(gòu)造函數(shù)。controller會(huì)在pre-linking步驟之前進(jìn)行初始化,并允許其他directive通過(guò)指定名稱的require進(jìn)行共享。這將允許directive之間相互溝通,增強(qiáng)相互之間的行為。controller默認(rèn)注入了以下本地對(duì)象:
    • $scope 與當(dāng)前元素結(jié)合的scope
    • $element 當(dāng)前的元素
    • $attrs 當(dāng)前元素的屬性對(duì)象
    • $transclude 一個(gè)預(yù)先綁定到當(dāng)前scope的轉(zhuǎn)置linking function
  • require
    請(qǐng)求另外的controller,傳入當(dāng)前directive的linking function中。require需要傳入一個(gè)directive controller的名稱。如果找不到這個(gè)名稱對(duì)應(yīng)的controller,那么將會(huì)拋出一個(gè)error。名稱可以加入以下前綴:
    • ? 不要拋出異常。這將使得這個(gè)依賴變?yōu)橐粋€(gè)可選項(xiàng)
    • ^ 允許查找父元素的controller
  • restrict
    EACM的子集的字符串,它限制了directive為指定的聲明方式。如果省略的話,directive將僅僅允許通過(guò)屬性聲明
    • E 元素名稱:
    • A 屬性名:
    • C class名:
    • M 注釋:
  • template
    如果replace為true,則將模版內(nèi)容替換當(dāng)前的html元素,并將原來(lái)元素的屬性、class一并轉(zhuǎn)移;如果replace為false,則將模版元素當(dāng)作當(dāng)前元素的子元素處理。
  • templateUrl
    與template基本一致,但模版通過(guò)指定的url進(jìn)行加載。因?yàn)槟0婕虞d是異步的,所有compilation、linking都會(huì)暫停,等待加載完畢后再執(zhí)行。
  • replace
    如果設(shè)置為true,那么模版將會(huì)替換當(dāng)前元素,而不是作為子元素添加到當(dāng)前元素中。(為true時(shí),模版必須有一個(gè)根節(jié)點(diǎn)
  • transclude
    編譯元素的內(nèi)容,使它能夠被directive使用。需要在模版中配合ngTransclude使用。transclusion的有點(diǎn)是linking function能夠得到一個(gè)預(yù)先與當(dāng)前scope綁定的transclusion function。一般地,建立一個(gè)widget,創(chuàng)建獨(dú)立scope,transclusion不是子級(jí)的,而是獨(dú)立scope的兄弟級(jí)。這將使得widget擁有私有的狀態(tài),transclusion會(huì)被綁定到父級(jí)scope中。(上面那段話沒(méi)看懂。但實(shí)際實(shí)驗(yàn)中,如果通過(guò)調(diào)用myDirective,而transclude設(shè)置為true或者字符串且template中包含的時(shí)候,將會(huì)將的編譯結(jié)果插入到sometag的內(nèi)容中。如果any的內(nèi)容沒(méi)有被標(biāo)簽包裹,那么結(jié)果sometag中將會(huì)多了一個(gè)span。如果本來(lái)有其他東西包裹的話,將維持原狀。但如果transclude設(shè)置為’element’的話,any的整體內(nèi)容會(huì)出現(xiàn)在sometag中,且被p包裹)
    • true/false 轉(zhuǎn)換這個(gè)directive的內(nèi)容。(這個(gè)感覺(jué)上,是直接將內(nèi)容編譯后搬入指定地方)
    • ‘element’ 轉(zhuǎn)換整個(gè)元素,包括其他優(yōu)先級(jí)較低的directive。(像將整體內(nèi)容編譯后,當(dāng)作一個(gè)整體(外面再包裹p),插入到指定地方)
  • compile
    這里是compile function,將在下面實(shí)例詳細(xì)說(shuō)明
  • link
    這里是link function ,將在下面實(shí)例詳細(xì)講解。這個(gè)屬性僅僅是在compile屬性沒(méi)有定義的情況下使用。

關(guān)于scope

這里關(guān)于directive的scope為一個(gè)object時(shí),有更多的內(nèi)容非常有必要說(shuō)明一下。看下面的代碼:


scope: {
    name: '=',
    age: '=',
    sex: '@',
    say: '&'
}

這個(gè)scope中關(guān)于各種屬性的配置出現(xiàn)了一些奇怪的前綴符號(hào),有=@,&,那么這些符號(hào)具體的含義是什么呢?再看下面的代碼:


<div my-directive name="myName" age="myAge" sex="male" say="say()"></div>


function Controller($scope) {
    $scope.name = 'Pajjket';
    $scope.age = 99;
    $scope.sex = '我是男的';
    $scope.say = function() {
        alert('Hello,我是彈出消息');
    };
}

可以看出,幾種修飾前綴符的大概含義:

  • =: 指令中的屬性取值為Controller中對(duì)應(yīng)$scope上屬性的取值
  • @: 指令中的取值為html中的字面量/直接量
  • &: 指令中的取值為Controller中對(duì)應(yīng)$scope上的屬性,但是這個(gè)屬性必須為一個(gè)函數(shù)回調(diào)

下面是更加官方的解釋:

  • =或者=expression/attr
    在本地scope屬性與parent scope屬性之間設(shè)置雙向的綁定。如果沒(méi)有指定attr名稱,那么本地名稱將與屬性名稱一致。
    例如:
    中,widget定義的scope為:{localModel: '=myAttr'},那么widget scope property中的localName將會(huì)映射父scope的parentModel。如果parentModel發(fā)生任何改變,localModel也會(huì)發(fā)生改變,反之亦然。即雙向綁定。
  • @或者@attr
    建立一個(gè)local scope property到DOM屬性的綁定。因?yàn)閷傩灾悼偸荢tring類型,所以這個(gè)值總返回一個(gè)字符串。如果沒(méi)有通過(guò)@attr指定屬性名稱,那么本地名稱將與DOM屬性的名稱一致。
    例如:
    ,widget的scope定義為:{localName: '@myAttr'}。那么,widget scope property的localName會(huì)映射出"hello "轉(zhuǎn)換后的真實(shí)值。當(dāng)name屬性值發(fā)生改變后,widget scope的localName屬性也會(huì)相應(yīng)的改變(僅僅是單向,與上面的=不同)。那么屬性是在父scope讀取的(不是從組件的scope讀取的)
  • &或者&attr
    提供一個(gè)在父scope上下文中執(zhí)行一個(gè)表達(dá)式的途徑。如果沒(méi)有指定attr的名稱,那么local name將與屬性名一致。
    例如:
    <widget my-attr="count = count + value">,widget的scope定義為:{localFn:’increment()’},那么isolate scope property localFn會(huì)指向一個(gè)包裹著increment()表達(dá)式的function。
    一般來(lái)說(shuō),我們希望通過(guò)一個(gè)表達(dá)式,將數(shù)據(jù)從isolate scope傳到parent scope中。這可以通過(guò)傳送一個(gè)本地變量鍵值的映射到表達(dá)式的wrapper函數(shù)中來(lái)完成。例如,如果表達(dá)式是increment(amount),那么我們可以通過(guò)localFn({amount:22})的方式調(diào)用localFn以指定amount的值。

directive 實(shí)例講解

下面的示例都圍繞著上面所作的參數(shù)說(shuō)明而展開(kāi)的。

directive聲明實(shí)例


// 自定義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';
    }
]);
var app = angular.module('testApp', [
    'directives',
    'controllers'
]);


<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>

template與templateUrl的區(qū)別和聯(lián)系

templateUrl其實(shí)根template功能是一樣的,只不過(guò)templateUrl加載一個(gè)html文件,上例中,我們也能發(fā)現(xiàn)問(wèn)題,template后面根的是html的標(biāo)簽,如果標(biāo)簽很多呢,那就比較不爽了??梢詫⑸侠械模瑃emplate改一下。


myDirective.directive('myTest', function() {
    return {
        restrict: 'EMAC',
        require: '^ngModel',
        scope: {
            ngModel: '='
        },
        templateUrl:'../partials/tem1.html'   //tem1.html中的內(nèi)容就是上例中template的內(nèi)容。
    }
});

scope重定義


//directives.js中定義myAttr
myDirectives.directive('myAttr', function() {
    return {
        restrict: 'E',
        scope: {
            customerInfo: '=info'
        },
        template: 'Name: {{customerInfo.name}} Address: {{customerInfo.address}}<br>' +
                  'Name: {{vojta.name}} Address: {{vojta.address}}'
    };
});
//controller.js中定義attrtest
myControllers.controller('attrtest',['$scope',
    function($scope) {
        $scope.naomi = {
            name: 'Naomi',
            address: '1600 Amphitheatre'
        };
        $scope.vojta = {
            name: 'Vojta',
            address: '3456 Somewhere Else'
        };
    }
]);


// html中
<body ng-app="testApp">
    <div ng-controller="attrtest">
        <my-attr info="naomi"></my-attr>
    </div>
</body>

其運(yùn)行結(jié)果如下:


Name: Naomi Address: 1600 Amphitheatre      //有值,因?yàn)閏ustomerInfo定義過(guò)的
Name: Address:                              //沒(méi)值 ,因?yàn)閟cope重定義后,vojta是沒(méi)有定義的

我們將上面的directive簡(jiǎn)單的改一下,


myDirectives.directive('myAttr', function() {
    return {
        restrict: 'E',
        template: 'Name: {{customerInfo.name}} Address: {{customerInfo.address}}<br>' +
                  'Name: {{vojta.name}} Address: {{vojta.address}}'
    };
});

運(yùn)行結(jié)果如下:


Name: Address:
Name: Vojta Address: 3456 Somewhere Else

因?yàn)榇藭r(shí)的directive沒(méi)有定義獨(dú)立的scope,customerInfo是undefined,所以結(jié)果正好與上面相反。

transclude的使用

transclude的用法,有點(diǎn)像jquery里面的$().html()功能


myDirective.directive('myEvent', function() {
    return {
        restrict: 'E',
        transclude: true,
        scope: {
            'close': '$onClick'      //根html中的on-click="hideDialog()"有關(guān)聯(lián)關(guān)系
        },
        templateUrl: '../partials/event_part.html'
    };
});
myController.controller('eventTest', [
    '$scope',
    '$timeout',
    function($scope, $timeout) {
        $scope.name = 'Tobias';
        $scope.hideDialog = function() {
            $scope.dialogIsHidden = true;
            $timeout(function() {
                $scope.dialogIsHidden = false;
            }, 2000);
        };
    }
]);


<body ng-app="phonecatApp">
    <div ng-controller="eventtest">
        <my-event ng-hide="dialogIsHidden" on-click="hideDialog()">
            Check out the contents, {{name}}!
        </my-event>
    </div>
</body>


<!--event_part.html -->
<div>
    <a href ng-click="close()">×</a>
    <div ng-transclude></div>
</div>

說(shuō)明:這段html最終的結(jié)構(gòu)應(yīng)該如下所示:


<body ng-app="phonecatApp">
    <div ng-controller="eventtest">
        <div ng-hide="dialogIsHidden" on-click="hideDialog()">
            <span>Check out the contents, {{name}}!</span>
        </div>
    </div>
</body>

將原來(lái)的html元素中的元素Check out the contents, !插入到模版的<div ng-transclude></div>中,還會(huì)另外附加一個(gè)<span>標(biāo)簽。

controller,link,compile之間的關(guān)系


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 ";
                }
            };
        }
    }
});


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


//html
<body ng-app="testApp">
    <div ng-controller="directive2">
        <example-directive></example-directive>
    </div>
</body>

上面小例子的運(yùn)行結(jié)果如下:


Hello 1111 22222 44444 5555 !

由結(jié)果可以看出來(lái),controller先運(yùn)行,compile后運(yùn)行,link不運(yùn)行。

我們現(xiàn)在將compile屬性注釋掉后,得到的運(yùn)行結(jié)果如下:


Hello 1111 22222 33333 !

由結(jié)果可以看出來(lái),controller先運(yùn)行,link后運(yùn)行,link和compile不兼容。一般地,compile比link的優(yōu)先級(jí)要高。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)