欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

AngularJs directive詳解及示例代碼

 更新時(shí)間:2016年09月01日 16:11:07   作者:Lcllao  
本文主要講解AngularJs directive的知識(shí),這里整理了相關(guān)資料及示例代碼,有興趣的小伙伴可以參考下

Directive是教HTML玩一些新把戲的途徑。在DOM編譯期間,directives匹配HTML并執(zhí)行。這允許directive注冊行為或者轉(zhuǎn)換DOM結(jié)構(gòu)。

  Angular自帶一組內(nèi)置的directive,對(duì)于建立Web App有很大幫助。繼續(xù)擴(kuò)展的話,可以在HTML定義領(lǐng)域特定語言(domain specific language ,DSL)。

一、在HTML中引用directives

  Directive有駝峰式(camel cased)的風(fēng)格的命名,如ngBind(放在屬性里貌似用不了~)。但directive也可以支蛇底式的命名(snake case),需要通過:(冒號(hào))、-(減號(hào))或_(下劃線)連接。作為一個(gè)可選項(xiàng),directive可以用“x-”或者“data-”作為前綴,以滿足HTML驗(yàn)證需要。這里列出directive的合法命名:

  1. ng:bind
  2. ng-bind
  3. ng_bind
  4. x-ng-bind
  5. data-ng-bind

  Directive可以放置于元素名、屬性、class、注釋中。下面是引用myDir這個(gè)directive的等價(jià)方式。(但很多directive都限制為“屬性”的使用方式)

<span my-dir="exp"></span>

<span class="my-dir: exp;"></span>

<my-dir></my-dir>

<!-- directive: my-dir exp -->

  Directive可以通過多種方式引用,下面列出N種等價(jià)的方式:

<!DOCTYPE HTML>
<html lang="zh-cn" ng-app>
<head>
 <meta charset="UTF-8">
 <title>invoke-directive</title>
 <style type="text/css">
  .ng-cloak {
   display: none;
  }
 </style>
</head>
<body>
<div ng-controller="MyCtrl">
 Hello <input ng-model="name"/><hr/>
 ngBind="name" 這個(gè)用不了~~ <span ngBind="name"></span><br/>
 ng:bind="name"<span ng:bind="name"></span><br/>
 ng_bind="name"<span ng_bind="name"></span><br/>
 ng-bind="name"<span ng-bind="name"></span><br/>
 data-ng-bind="name"<span data-ng-bind="name"></span><br/>
 x-ng-bind="name"<span x-ng-bind="name"></span><br/>
</div>
<script src="../angular-1.0.1.js" type="text/javascript"></script>
<script type="text/javascript">
 function MyCtrl($scope) {
  $scope.name = "beauty~~";
 }
</script>
</body>
</html>

二、String interpolation

  在編譯過程中,compiler通過$interpolate服務(wù)匹配文本與屬性中的嵌入表達(dá)式(如{{something}})。這些表達(dá)式將會(huì)注冊為watches,并且作為digest cycle(之前不是digest-loop嗎?!)的一部分,一同更新。下面是一個(gè)簡單的interpolation:

<img src="img/{{username}}.jpg"/>Hello {{username}}!

三、Compilation process, and directive matching

  HTML“編譯”的三個(gè)步驟:

  1. 首先,通過瀏覽器的標(biāo)準(zhǔn)API,將HTML轉(zhuǎn)換為DOM對(duì)象。這是很重要的一步。因?yàn)槟0姹仨毷强山馕觯ǚ弦?guī)范)的HTML。這里可以跟大多數(shù)的模版系統(tǒng)做對(duì)比,它們一般是基于字符串的,而不是基于DOM元素的。

  2. 對(duì)DOM的編譯(compilation)是通過調(diào)用$comple()方法完成的。這個(gè)方法遍歷DOM,對(duì)directive進(jìn)行匹配。如果匹配成功,那么它將與對(duì)應(yīng)的DOM一起,加入到directive列表中。只要所有與指定DOM關(guān)聯(lián)的directive被識(shí)別出來,他們將按照優(yōu)先級(jí)排序,并按照這個(gè)順序執(zhí)行他們的compile() 函數(shù)。directive的編譯函數(shù)(compile function),擁有一個(gè)修改DOM結(jié)構(gòu)的機(jī)會(huì),并負(fù)責(zé)產(chǎn)生link() function的解析。$compile()方法返回一個(gè)組合的linking function,是所有directive自身的compile function返回的linking function的集合。

  3. 通過上一步返回的linking function,將模版與scope連接起來。這反過來會(huì)調(diào)用directive自身的linking function,允許它們在元素上注冊一些監(jiān)聽器(listener),以及與scope一起建立一些watches。這樣得出的結(jié)果,是在scope與DOM之間的一個(gè)雙向、即時(shí)的綁定。scope發(fā)生改變時(shí),DOM會(huì)得到對(duì)應(yīng)的響應(yīng)。

 var $compile = ...; // injected into your code

 var scope = ...;

 var html = '<div ng-bind='exp'></div>';

 // Step 1: parse HTML into DOM element
 var template = angular.element(html);

 // Step 2: compile the template
 var linkFn = $compile(template);

 // Step 3: link the compiled template with the scope.
 linkFn(scope);


四、Reasons behind the compile/link separation

  在這個(gè)時(shí)候,你可能會(huì)想知道為什么編譯進(jìn)程會(huì)劃分為compile和linke兩個(gè)步驟。為了明白這一點(diǎn),讓我們看看一個(gè)真實(shí)的例子(repeater)

 Hello {{user}}, you have these actions:
 <ul>
  <li ng-repeat="action in user.actions">{{action.description}}</li>
 </ul>

  簡單地講,之所以分開compile和linke兩步,是因?yàn)橛袝r(shí)候需要在model改變后,對(duì)應(yīng)的DOM結(jié)構(gòu)也需要改變的情況,如repeaters。

  當(dāng)上面的例子被編譯時(shí),編譯器會(huì)遍歷所有節(jié)點(diǎn)以尋找directive。{{user}}是一個(gè)interpolation directive的例子。ngRepeat又是另外一個(gè)directive。但ngRepeat有一個(gè)難點(diǎn)。它需要能夠很快地為每一個(gè)在users.actions中的action制造出新的li的能力。這意味著它為了滿足克隆li并且嵌入特定的action(這里是指user的actions的其中一個(gè)值)的目的,需要保持一個(gè)干凈li元素的拷貝,li元素需要被克隆和插入ul元素。但僅僅克隆li元素是不夠的。還需要編譯li,以便它的directive({{action.descriptions}})能夠在正確的scope中被解析。原始的方法,一般會(huì)簡單地插入一個(gè)li元素的拷貝,然后編譯它。但編譯每一個(gè)li元素的拷貝會(huì)比較緩慢,因?yàn)榫幾g過程需要我們遍歷DOM節(jié)點(diǎn)樹,查找directive并運(yùn)行它們。如果我們有一個(gè)編譯,需要通過repeater對(duì)100個(gè)item進(jìn)行處理,那么我們將陷入性能問題。

  問題的解決方案,是將編譯過程分解為兩個(gè)步驟。compile階段識(shí)別出所有directive,并且將它們按照優(yōu)先級(jí)進(jìn)行排序,在linking階段將特定的scope與特定的li綁定在一起。

  ngRepeat將各個(gè)li分開編譯以防止編譯過程落入li元素中。li元素的編譯結(jié)果是一個(gè)包含所有包含在li元素中的directive的linking function,準(zhǔn)備與特定li元素的拷貝進(jìn)行連接。在運(yùn)行時(shí),ngRepeat監(jiān)測表達(dá)式,并作為一個(gè)item,被加入到一個(gè)li元素拷貝的數(shù)組,為克隆好的li元素創(chuàng)建新的scope,并調(diào)用該拷貝對(duì)應(yīng)的link function。

  總結(jié):

1.編譯函數(shù)(compile function) - 編譯函數(shù)在directive中是比較少見的,因?yàn)榇蠖鄶?shù)directive只關(guān)心與指定的DOM元素工作,而不是改變DOM元素的模版(DOM自身以及內(nèi)部的結(jié)構(gòu))。為了優(yōu)化性能,一些可以被directive實(shí)例共享的操作,可以移動(dòng)到compile函數(shù)中。

2.連接函數(shù)(link function) - 極少directive是沒有l(wèi)ink function的。link function允許directive在指定的拷貝后的DOM元素實(shí)例上注冊監(jiān)聽器,也可以將scope中特定內(nèi)容復(fù)制到DOM中。

五、寫一個(gè)directive(簡易版)

  在這個(gè)例子里面,我們將建立一個(gè)根據(jù)輸入格式,顯示當(dāng)前時(shí)間的directive。

<!DOCTYPE HTML>
<html lang="zh-cn" ng-app="TimeFormat">
<head>
 <meta charset="UTF-8">
 <title>time-format</title>
</head>
<body>
<div ng-controller="MyCtrl" id="main">
 Date format: <input ng-model="format" type="text"/><hr/>
 <!--下面使用屬性x-current-time,是為了試試上面說的合法命名~~current:time、current-time、current_time、data-current-time -_-!!! -->
 Current time is : <span x-current-time="format" id="myFormat"></span><br/>
 <button ng-click="remove()">remove the span</button>
</div>
<script src="../angular-1.0.1.js" type="text/javascript"></script>
<script type="text/javascript">
 angular.module("TimeFormat", [])
  //在TimeFormat應(yīng)用中注冊“currentTime”這個(gè)directive的工廠方法
  //前文提到過,依賴注入,可以直接在function的參數(shù)中寫入,這里注入了$timeout、dataFilter
   .directive("currentTime", function (dateFilter) {
    //這個(gè)是上面提到的linking function。(不需要添加compile function,為啥?。。。)
    return function (scope, element, attr) {
     var intervalId;
     //更新對(duì)應(yīng)element的text值,即更新時(shí)間
     function updateTime() {
      element.text(dateFilter(new Date(), scope.format));
     }
     //通過watch,監(jiān)控span對(duì)象的currentTime的值(是format這個(gè)model值,即input的值??!)
     //這個(gè)方法僅僅在format發(fā)生改變的時(shí)候執(zhí)行
     scope.$watch(attr.currentTime, function (value) {
      scope.format = value;
      updateTime();
     });
     //當(dāng)span被去掉的時(shí)候,取消更新
     element.bind("$destroy", function () {
      clearInterval(intervalId);
     });

     intervalId = setInterval(updateTime, 1000);
    };
   }).controller("MyCtrl",function($scope,$rootScope) {
    $scope.format = "M/d/yy h:mm:ss a";
    $scope.remove = function() {
     var oFormat = document.getElementById("myFormat");
     if(oFormat) {
      angular.element(oFormat).remove();//通過這種方式調(diào)用remove,可以觸發(fā)$destroy事件?。。。≡嚵宋襈久。。。
     }
    };
   });
</script>
</body>
</html>


 

六、寫一個(gè)directive(詳細(xì)版)

  下面是一個(gè)創(chuàng)建directive樣例(directive對(duì)象定義模版)。想看詳細(xì)列表,請繼續(xù)往下看。

var myModule = angular.module(...);

  

myModule.directive('directiveName', function factory(injectables) {

 var directiveDefinitionObject = {

   priority: 0,

   template: '<div></div>',

   templateUrl: 'directive.html',

   replace: false,

   transclude: false,

   restrict: 'A',

   scope: false,

   compile: function compile(tElement, tAttrs, transclude) {

     return {

       pre: function preLink(scope, iElement, iAttrs, controller) { ... },

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

     }

   },

   link: function postLink(scope, iElement, iAttrs) { ... }

 };

 return directiveDefinitionObject;

 });

  在大多數(shù)場景下,我們并不需要精確控制,所以上面的定義是可以化簡的。定義模版中的每一部分,將在下面章節(jié)講解。在這個(gè)章節(jié),我們僅僅關(guān)注定義模版的異構(gòu)體(isomers of this skeleton,沒看懂。。。期待大家補(bǔ)充)。

  簡化代碼的第一步是依賴默認(rèn)值。因此,上面的代碼可以簡化為:

var myModule = angular.module(...);
  
 myModule.directive('directiveName', function factory(injectables) {
   var directiveDefinitionObject = {
     compile: function compile(tElement, tAttrs) {
       return function postLink(scope, iElement, iAttrs) { ... }
     }
   };
   return directiveDefinitionObject;
 });

  大多數(shù)directive只關(guān)心實(shí)例,而不是模版轉(zhuǎn)換,所以可以進(jìn)一步化簡(翻譯得很勉強(qiáng)。。。期待大家補(bǔ)充):

var myModule = angular.module(...);
  
 myModule.directive('directiveName', function factory(injectables) {
   return function postLink(scope, iElement, iAttrs) { ... }
 });
 

七、工廠方法

  工廠方法負(fù)責(zé)創(chuàng)建directive。它僅僅使用一次,就在compiler第一次匹配到directive的時(shí)候。你可以在這里執(zhí)行一些初始化操作。工廠方法通過$injector.invoke執(zhí)行,讓它遵守所有注入聲明規(guī)則(rules of injection annotation),讓其變?yōu)榭勺⑷氲摹?/p>

八、directive定義對(duì)象說明

  directive定義對(duì)象提供了compiler的結(jié)構(gòu)。屬性如下:

1.name - 當(dāng)前scope的名稱,注冊時(shí)可以使用默認(rèn)值(不填)。

2.priority(優(yōu)先級(jí))- 當(dāng)有多個(gè)directive定義在同一個(gè)DOM元素時(shí),有時(shí)需要明確它們的執(zhí)行順序。這屬性用于在directive的compile function調(diào)用之前進(jìn)行排序。如果優(yōu)先級(jí)相同,則執(zhí)行順序是不確定的(經(jīng)初步試驗(yàn),優(yōu)先級(jí)高的先執(zhí)行,同級(jí)時(shí)按照類似棧的“后綁定先執(zhí)行”。另外,測試時(shí)有點(diǎn)不小心,在定義directive的時(shí)候,兩次定義了一個(gè)相同名稱的directive,但執(zhí)行結(jié)果發(fā)現(xiàn),兩個(gè)compile或者link function都會(huì)執(zhí)行)。 

3.terminal(最后一組)- 如果設(shè)置為”true”,則表示當(dāng)前的priority將會(huì)成為最后一組執(zhí)行的directive。任何directive與當(dāng)前的優(yōu)先級(jí)相同的話,他們依然會(huì)執(zhí)行,但順序是不確定的(雖然順序不確定,但基本上與priority的順序一致。當(dāng)前優(yōu)先級(jí)執(zhí)行完畢后,更低優(yōu)先級(jí)的將不會(huì)再執(zhí)行)。

4.scope - 如果設(shè)置為:

              1).true - 將為這個(gè)directive創(chuàng)建一個(gè)新的scope。如果在同一個(gè)元素中有多個(gè)directive需要新的scope的話,它還是只會(huì)創(chuàng)建一個(gè)scope。新的作用域規(guī)則不適用于根模版(root of the template),因此根模版往往會(huì)獲得一個(gè)新的scope。 

              2).{}(object hash) - 將創(chuàng)建一個(gè)新的、獨(dú)立(isolate)的scope?!眎solate” scope與一般的scope的區(qū)別在于它不是通過原型繼承于父scope的。這對(duì)于創(chuàng)建可復(fù)用的組件是很有幫助的,可以有效防止讀取或者修改父級(jí)scope的數(shù)據(jù)。這個(gè)獨(dú)立的scope會(huì)創(chuàng)建一個(gè)擁有一組來源于父scope的本地scope屬性(local scope properties)的object hash。這些local properties對(duì)于為模版創(chuàng)建值的別名很有幫助(useful for aliasing values for templates -_-!)。本地的定義是對(duì)其來源的一組本地scope property的hash映射(Locals definition is a hash of local scope property to its source #&)$&@#)($&@#_): 

               3).@或@attr - 建立一個(gè)local scope property到DOM屬性的綁定。因?yàn)閷傩灾悼偸荢tring類型,所以這個(gè)值總是返回一個(gè)字符串。如果沒有通過@attr指定屬性名稱,那么本地名稱將與DOM屬性的名稱一直。例如<widget my-attr=”hello {{name}}”>,widget的scope定義為:{localName:'@myAttr'}。那么,widget scope property的localName會(huì)映射出”hello {{name}}"轉(zhuǎn)換后的真實(shí)值。name屬性值改變后,widget scope的localName屬性也會(huì)相應(yīng)地改變(僅僅單向,與下面的”=”不同)。name屬性是在父scope讀取的(不是組件scope) 

               4).=或=expression(這里也許是attr) - 在本地scope屬性與parent scope屬性之間設(shè)置雙向的綁定。如果沒有指定attr名稱,那么本地名稱將與屬性名稱一致。例如<widget my-attr=”parentModel”>,widget定義的scope為:{localModel:'=myAttr'},那么widget scope property “l(fā)ocalName”將會(huì)映射父scope的“parentModel”。如果parentModel發(fā)生任何改變,localModel也會(huì)發(fā)生改變,反之亦然。(雙向綁定)

                5).&或&attr - 提供一個(gè)在父scope上下文中執(zhí)行一個(gè)表達(dá)式的途徑。如果沒有指定attr的名稱,那么local name將與屬性名稱一致。例如<widget my-attr=”count = count + value”>,widget的scope定義為:{localFn:'increment()'},那么isolate scope property “l(fā)ocalFn”會(huì)指向一個(gè)包裹著increment()表達(dá)式的function。一般來說,我們希望通過一個(gè)表達(dá)式,將數(shù)據(jù)從isolate scope傳到parent scope中。這可以通過傳送一個(gè)本地變量鍵值的映射到表達(dá)式的wrapper函數(shù)中來完成。例如,如果表達(dá)式是increment(amount),那么我們可以通過localFn({amount:22})的方式調(diào)用localFn以指定amount的值(上面的例子真的沒看懂,&跑哪去了?)。

5.controller - controller 構(gòu)造函數(shù)。controller會(huì)在pre-linking步驟之前進(jìn)行初始化,并允許其他directive通過指定名稱的require進(jìn)行共享(看下面的require屬性)。這將允許directive之間相互溝通,增強(qiáng)相互之間的行為。controller默認(rèn)注入了以下本地對(duì)象:

                 1).$scope - 與當(dāng)前元素結(jié)合的scope 

                 2).$element - 當(dāng)前的元素 

                 3).$attrs - 當(dāng)前元素的屬性對(duì)象

                4).$transclude - 一個(gè)預(yù)先綁定到當(dāng)前轉(zhuǎn)置scope的轉(zhuǎn)置linking function :function(cloneLinkingFn)。(A transclude linking function pre-bound to the correct transclusion scope)

6.require - 請求另外的controller,傳入當(dāng)前directive的linking function中。require需要傳入一個(gè)directive controller的名稱。如果找不到這個(gè)名稱對(duì)應(yīng)的controller,那么將會(huì)拋出一個(gè)error。名稱可以加入以下前綴:

               1).? - 不要拋出異常。這使這個(gè)依賴變?yōu)橐粋€(gè)可選項(xiàng)。

              2).^ - 允許查找父元素的controller

7.restrict - EACM的子集的字符串,它限制directive為指定的聲明方式。如果省略的話,directive將僅僅允許通過屬性聲明: 

                  1)E - 元素名稱: <my-directive></my-directive> 

                 2).A - 屬性名: <div my-directive=”exp”></div>

                3). C - class名: <div class=”my-directive:exp;”></div> 

                4).M - 注釋 : <!-- directive: my-directive exp -->

8.template - 如果replace 為true,則將模版內(nèi)容替換當(dāng)前的HTML元素,并將原來元素的屬性、class一并遷移;如果為false,則將模版元素當(dāng)作當(dāng)前元素的子元素處理。想了解更多的話,請查看“Creating Widgets”章節(jié)(在哪啊。。。Creating Components就有。。。) 

9.templateUrl - 與template基本一致,但模版通過指定的url進(jìn)行加載。因?yàn)槟0婕虞d是異步的,所以compilation、linking都會(huì)暫停,等待加載完畢后再執(zhí)行。 

10.replace - 如果設(shè)置為true,那么模版將會(huì)替換當(dāng)前元素,而不是作為子元素添加到當(dāng)前元素中。(注:為true時(shí),模版必須有一個(gè)根節(jié)點(diǎn))

11.transclude - 編譯元素的內(nèi)容,使它能夠被directive所用。需要(在模版中)配合ngTransclude使用(引用)。transclusion的優(yōu)點(diǎn)是linking function能夠得到一個(gè)預(yù)先與當(dāng)前scope綁定的transclusion function。一般地,建立一個(gè)widget,創(chuàng)建isolate scope,transclusion不是子級(jí)的,而是isolate scope的兄弟。這將使得widget擁有私有的狀態(tài),transclusion會(huì)被綁定到父級(jí)(pre-isolate)scope中。(上面那段話沒看懂。但實(shí)際實(shí)驗(yàn)中,如果通過<any my-directive>{{name}}</any my-directive>調(diào)用myDirective,而transclude設(shè)置為true或者字符串且template中包含<sometag ng-transclude>的時(shí)候,將會(huì)將{{name}}的編譯結(jié)果插入到sometag的內(nèi)容中。如果any的內(nèi)容沒有被標(biāo)簽包裹,那么結(jié)果sometag中將會(huì)多了一個(gè)span。如果本來有其他東西包裹的話,將維持原狀。但如果transclude設(shè)置為'element'的話,any的整體內(nèi)容會(huì)出現(xiàn)在sometag中,且被p包裹)

                  1).true - 轉(zhuǎn)換這個(gè)directive的內(nèi)容。(這個(gè)感覺上,是直接將內(nèi)容編譯后搬入指定地方)

                  2).‘element' - 轉(zhuǎn)換整個(gè)元素,包括其他優(yōu)先級(jí)較低的directive。(像將整體內(nèi)容編譯后,當(dāng)作一個(gè)整體(外面再包裹p),插入到指定地方) 

12.compile - 這里是compile function,將在下面章節(jié)詳細(xì)講解

13.link - 這里是link function ,將在下面章節(jié)詳細(xì)講解。這個(gè)屬性僅僅是在compile屬性沒有定義的情況下使用。

九、Compile function

function compile ( tElement, tAttrs, transclude) { … }

  compile function用于處理DOM模版的轉(zhuǎn)換。由于大多數(shù)directive都不需要轉(zhuǎn)換模版,所以compile不會(huì)經(jīng)常被使用到。需要compile function的directive,一般是那種需要轉(zhuǎn)換DOM模版的(如ngRepeat),或者是需要異步加載內(nèi)容的(如ngView)。compile function擁有一下的參數(shù):

1.tElement - 模版元素 – 使用當(dāng)前directive的元素。僅僅在當(dāng)前元素或者當(dāng)前元素子元素下做模版轉(zhuǎn)換是安全的。

2.tAttrs - 模版屬性 - 標(biāo)準(zhǔn)化的屬性,在當(dāng)前元素聲明的,可以在各個(gè)directive之間共享。詳情請看Attributes章節(jié)

3.transclude – 一個(gè)轉(zhuǎn)換用的linking function:  function(scope,cloneLinking).

  注意:如果template被克隆過,那么template實(shí)例和link實(shí)例不能是同一個(gè)object。為此,在compile function中做任何除了DOM轉(zhuǎn)換以外的事情,是不安全的,這將應(yīng)用到所有克隆中。特別是,DOM事件監(jiān)聽器的注冊的操作,應(yīng)該在linking function中進(jìn)行,而不是compile function。

  compile function 可以有一個(gè)返回值,類型可以是function或者object。

1.返回function – 通常在不需要compile function(為空)時(shí)使用,等同于通過link(directive定義模版的屬性)注冊linking function。

2.返回包含pre、post屬性的object - 允許我們控制在linking phase期間何時(shí)調(diào)用linking function。詳情請看下面關(guān)于pre-linking、post-linking function的章節(jié)。

十、Linking function

function link( scope, iElement, iAttrs, controller) { … }
  link function負(fù)責(zé)注冊DOM事件監(jiān)聽器,也可以進(jìn)行DOM的更新操作。link function會(huì)在模版克隆操作完畢之后執(zhí)行。這里存放著directive大多數(shù)的邏輯。

1.scope - Scope - 被directive用于注冊watches(http://docs.angularjs.org/api/ng.$rootScope.Scope#$watch)。

2.iElement - 元素實(shí)例 - directive使用的元素。只有在postLink function里面對(duì)子元素進(jìn)行操作,才是安全的。因?yàn)樽釉匾呀?jīng)被link(與model連接嗎??。?br />

3.iAttrs - 屬性實(shí)例 - 標(biāo)準(zhǔn)的當(dāng)前元素的屬性列表。在所有directive的linking function之間分享的。

4.controller - controller實(shí)例 - 如果在當(dāng)前元素的directive中,有其中一個(gè)定義了controller,那么可以在這里獲取到該controller的實(shí)例。這個(gè)controller是在所有directive之間共享的,允許各個(gè)directive將controller當(dāng)作一個(gè)它們之間溝通頻道。

Pre-link function

  在子元素linked之前執(zhí)行。在這里做DOM轉(zhuǎn)換是不安全的,因?yàn)閏ompiler的linking function在link時(shí)可能會(huì)定位不到正確的元素。

Post-link function

  在子元素linked之后執(zhí)行。在這里執(zhí)行DOM轉(zhuǎn)換是安全的。

十一、Attributes

  attribute object - 在link()或compile()中作為參數(shù) - 是一個(gè)訪問以下東東的途徑:

1.標(biāo)準(zhǔn)化的屬性名稱:由于directive,例如ngBind,可以表現(xiàn)為多種形式,如”ng:bind”、”x-ng-bind”……這個(gè)attribute對(duì)象允許我們通過標(biāo)準(zhǔn)命名(駝峰式)訪問屬性。

2.directive 之間通訊:所有directive共享一個(gè)attribute對(duì)象實(shí)例,使得directive可以通過attribute 對(duì)象進(jìn)行directive之間的內(nèi)部通訊。

3.支持interpolation:interpolation屬性是分配到attribute對(duì)象中,允許其他directive讀取該interpolated value。

4.觀察interpolated屬性:通過attr.$observe去觀察屬性值的變化,包括interpolation(例如src=”{{bar}}”)。不單單很有效,而且是簡單地獲取真實(shí)值唯一的辦法。因?yàn)樵趌inking階段,interpolation還沒被賦值(替換真實(shí)值),所以在這時(shí)候訪問它,結(jié)果是undefined。


<!DOCTYPE HTML>
<html lang="zh-cn" ng-app="DirectiveProperty">
<head>
 <meta charset="UTF-8">
 <title>directive-attribute-test</title>
 <style type="text/css">
  .ng-cloak {
   display: none;
  }
 </style>
</head>
<body ng-controller="MyCtrl">
<input type="text" ng-model="name" value="myName"/>
<p my-attr="123" directive-p2 attr-dd="{{name}}"></p>
<script src="../angular-1.0.1.js" type="text/javascript"></script>
<script type="text/javascript">
 var app = angular.module("DirectiveProperty", []);
 app.controller("MyCtrl", function ($scope) {
  $scope.name = "my little dada";
 });
 var directiveP2 = app.directive("directiveP2", function () {
  return {
   link:function postLink(scope,lEle,lAttr) {    console.log("myAttr:" + lAttr.myAttr);//123
    console.log("myAttr:" + lAttr.attrDd);//undefinded
    lAttr.$observe('attrDd', function(value) {
     console.log('attrDd has changed value to ' + value); //my little dada
     //除此以外,還可啥辦法可以拿到這個(gè)值啊。。。¥&()@#&¥(@#
    });
   }
  };
 });
</script>
</body>
</html>

十二、理解transclusion和scope

  我們經(jīng)常需要一些可重用的組件。下面是一段偽代碼,展示一個(gè)簡單的dialog組件如何能夠工作。

 <button ng-click="show=true">show</button>

 <dialog title="Hello {{username}}."

  visible="show"

  on-cancel="show = false"

  on-ok="show = false; doSomething()">

  Body goes here: {{username}} is {{title}}.

</dialog>

  點(diǎn)擊“show”按鈕將會(huì)打開dialog。dialog有一個(gè)標(biāo)題,與數(shù)據(jù)“username”綁定,還有一段我們想放置在dialog內(nèi)部的內(nèi)容。
  下面是一段為了dialog而編寫的模版定義:

<div ng-show="show()">

 <h3>{{title}}</h3>

 <div class="body" ng-transclude></div>

 <div class="footer">

  <button ng-click="onOk()">Save changes</button>

  <button ng-click="onCancel()">Close</button>

 </div>

</div>

  這將無法正確渲染,除非我們對(duì)scope做一些特殊處理。

  第一個(gè)我們需要解決的問題是,dialog模版期望title會(huì)被定義,而在初始化時(shí)會(huì)與username綁定。此外,按鈕需要onOk、onCancel兩個(gè)function出現(xiàn)在scope里面。這限制了插件的效能(this limits the usefulness of the widget。。。)。為了解決映射問題,通過如下的本地方法(locals,估計(jì)在說directive定義模版中的scope)去創(chuàng)建模版期待的本地變量:

scope :{

 title: 'bind', // set up title to accept data-binding

 onOk: 'expression', // create a delegate onOk function

 onCancel: 'expression', // create a delegate onCancel function

 show: 'accessor' // create a getter/setter function for visibility.

}

  在控件scope中創(chuàng)建本地屬性,帶來了兩個(gè)問題:

  1. isolation(屬性隔離?) - 如果用戶忘記了在控件模版設(shè)置title這個(gè)元素屬性,那么title將會(huì)與祖先scope的屬性”title”(如有)綁定。這是不可預(yù)知、不可取的。

  2. transclusion - transcluded DOM可以查看控件的locals(isolate scope?),locals會(huì)覆蓋transclusion中真正需要綁定的屬性。在我們的例子中,插件中的title屬性破壞了transclusion的title屬性。

  為了解決這個(gè)缺乏屬性隔離的問題,我們需要為這個(gè)directive定義一個(gè)isolated scope。isoloted scope不是通過從child scope(這里為啥是child scope?不是parent scope嗎?)原型繼承的,所以我們無需擔(dān)心屬性沖突問題(作為當(dāng)前scope的兄弟)。

  然而,isolated scope帶來了一個(gè)新問題:如果transcluded DOM 是控件的isolated scope的一個(gè)子元素,那么他將不能與任何東西綁定(if a transcluded DOM is a child of the widget isolated scope then it will not be able to bind to anything)。因此,transcluded scope是原始scope的一個(gè)子scope,在控件為本地屬性而創(chuàng)建isolated scope之前創(chuàng)建的。transcluded與控件isolated scope屬于兄弟節(jié)點(diǎn)(scope樹中)。

  這也許看起來會(huì)有點(diǎn)意想不到的復(fù)雜,但這樣做讓控件用戶與控件開發(fā)者至少帶來了驚喜。(解決了問題)

  因此,最終的directive定義大致如下:

transclude:true,

scope :{

 title: 'bind', // set up title to accept data-binding

 onOk: 'expression', // create a delegate onOk function

 onCancel: 'expression', // create a delegate onCancel function
 
 show: 'accessor' // create a getter/setter function for visibility.

 //我試過這么整,失敗……請繼續(xù)往下看

}

  我嘗試把上面的代碼拼湊成一個(gè)完整的例子。直接復(fù)制的話,無法達(dá)到預(yù)期效果。但稍作修改后,插件可以運(yùn)行了。

<!DOCTYPE html>
<html ng-app="Dialog">
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
 <title>directive-dialog</title>
 <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
 <script src="../angular.js" type="text/javascript"></script>
</head>
<body>
 <div ng-controller="MyCtrl">
  <button ng-click="show=true">show</button>
  <dialog title="Hello {{username}}"
    visible="{{show}}"
    on-cancel="show=false;"
    on-ok="show=false;methodInParentScope();">
    <!--上面的on-cancel、on-ok,是在directive的isoloate scope中通過&引用的。如果表達(dá)式中包含函數(shù),那么需要將函數(shù)綁定在parent scope(當(dāng)前是MyCtrl的scope)中-->
   Body goes here: username:{{username}} , title:{{title}}.
   <ul>
    <!--這里還可以這么玩~names是parent scope的-->
    <li ng-repeat="name in names">{{name}}</li>
   </ul>
  </dialog>
 </div>
 <script type="text/javascript">
  var myModule = angular.module("Dialog", []);
  myModule.controller("MyCtrl", function ($scope) {
   $scope.names = ["name1", "name2", "name3"];
   $scope.show = false;
   $scope.username = "Lcllao";
   $scope.title = "parent title";
   $scope.methodInParentScope = function() {
    alert("記住。。scope里面通過&定義的東東,是在父scope中玩的!!。。。");
   };
  });
  myModule.directive('dialog', function factory() {
   return {
    priority:100,
    template:['<div ng-show="visible">',
     ' <h3>{{title}}</h3>',
     ' <div class="body" ng-transclude></div>',
     ' <div class="footer">',
     '  <button ng-click="onOk()">OK</button>',
     '  <button ng-click="onCancel()">Close</button>',
     ' </div>',
     '</div>'].join(""),
    replace:false,
    transclude: true,
    restrict:'E',
    scope:{
     title:"@",//引用dialog標(biāo)簽title屬性的值
     onOk:"&",//以wrapper function形式引用dialog標(biāo)簽的on-ok屬性的內(nèi)容
     onCancel:"&",//以wrapper function形式引用dialog標(biāo)簽的on-cancel屬性的內(nèi)容
     visible:"@"http://引用dialog標(biāo)簽visible屬性的值
    }
   };
  });
 </script>
</body>
</html>
 

十三、Creating Components

  我們通常會(huì)期望通過復(fù)雜的DOM結(jié)構(gòu)替換掉directive(所在的元素?目的大概是使directive內(nèi)部復(fù)雜點(diǎn),看起來牛點(diǎn)@_@)。這讓directive成為使用可復(fù)用組件的建立應(yīng)用的一個(gè)快捷方式。

  下面是一個(gè)可復(fù)用組件的例子:

<!DOCTYPE html>
<html ng-app="ZippyModule">
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
 <title>ZippyModule</title>
 <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
 <style type="text/css">
  .zippy {
   border: 1px solid black;
   display: inline-block;
   width: 250px;
  }
  .zippy.opened > .title:before { content: '▼ '; }
  .zippy.opened > .body { display: block; }
  .zippy.closed > .title:before { content: '► '; }
  .zippy.closed > .body { display: none; }
  .zippy > .title {
   background-color: black;
   color: white;
   padding: .1em .3em;
   cursor: pointer;
  }
  .zippy > .body {
   padding: .1em .3em;
  }
 </style>
 <script src="../angular.js" type="text/javascript"></script>
</head>
<body>
 <div ng-controller="MyCtrl">
  Title: <input ng-model="title" type="text"><br/>
  Text: <textarea ng-model="text" ></textarea>
  <hr/>
  <div class="zippy" zippy-title="Details: {{title}}...">{{text}}</div>
 </div>
 <script type="text/javascript">
  var myModule = angular.module("ZippyModule", []);
  myModule.controller("MyCtrl", function ($scope) {
   $scope.title = "這里是標(biāo)題";
   $scope.text = "這里是內(nèi)容哇。。。";
  });
  myModule.directive('zippy', function () {
   return {
    template: '<div>' +
      ' <div class="title">{{title}}</div>' +//這個(gè)title屬于當(dāng)前directive isolate scope的property
      ' <div class="body" ng-transclude></div>' + //這里的東西,獲取的是父scope的property咯
      '</div>',
    replace:true,
    transclude: true,
    restrict:'C',
    scope:{
     title:"@zippyTitle"http://綁定directive元素身上的zippy-title屬性
    },
    link:function(scope,element,attrs) {
     var title = angular.element(element.children()[0]),
       opened = false;

     title.bind("click", toogle);
     element.addClass("closed");

     function toogle() {
      opened = !opened;
      element.removeClass(opened ? "closed" : "opened");
      element.addClass(opened ? "opened" : "closed");
     }
    }
   };
  });
 </script>
</body>
</html>

相關(guān)文章

最新評(píng)論