AngularJs $compile編譯服務與指令


$compile

這是個編譯服務。編譯一段HTML字符串或者DOM的模板, 產生一個將scope和模板連接到一起的函數。

編譯服務主要是為指令編譯DOM元素,下面的一大段也是主要介紹指令的。

下面是一個被聲明的帶指令定義對象的指令的示例:

  var myModule = angular.module(...);
  myModule.directive('directiveName', [“injectables”,…,function factory(injectables,…) {
    var directiveDefinitionObject = {
      priority: 0,
      template: '<div></div>', // or // function(tElement, tAttrs) { ... },
      // or
      // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
      replace: false,
      transclude: false,
      restrict: 'A',
      scope: false,
      controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
      controllerAs: 'stringAlias',
      require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
      compile: function compile(tElement, tAttrs, transclude) {
      return {
       pre: function preLink(scope, iElement, iAttrs, controller) { ... },
       post: function postLink(scope, iElement, iAttrs, controller) { ... }
      }
      // or
      // return function postLink( ... ) { ... }
      },
      // or
      // link: {
       // pre: function preLink(scope, iElement, iAttrs, controller) { ... },
       // post: function postLink(scope, iElement, iAttrs, controller) { ... }
      // }
      // or
      // link: function postLink( ... ) { ... }
      };
    return directiveDefinitionObject;
  });

這是一個完整的指令,所返回的對象的各項屬性都寫在上面了,下面對於指令對象屬性的介紹,就不一一的寫示例代碼了,后面主要寫下簡單的就ok。

備注:任何為指定的參數都將設置為默認值。下面將會列出各默認值。

指令定義對象

指令定義的對象為編譯器提供說明,其屬性是:

priority
當有一個DOM元素中定義了多個指令,有時有必要指定的指令的應用順序。在編譯函數被調用之前,先將其按優先級排序。這個屬性的值越大,優先級越高,將會更先被編譯。具有相同優先級的指令的順序是未定義的。默認優先級為0。

如果該屬性設置為true,那么當前指令的優先將會放在最后一組將執行(在當前優先級的任何指令將仍然作為未定義優先級的設置執行)

scope

如果設置為false,則不會為指令創建任何scope。指令使用其父級scope。

如果設置為true,那么該指令將會創建一個新的子scope,這個scope繼承父級的屬性。如果在一個DOM的多個指令上定義新作用域,也只能創建一個新的作用域。模板有了新的作用域后,該作用域的規則不適用與模板所在的根節點。

如果設置為{},那么將會創建一個新的、隔離的作用域。該作用域不繼承其父作用域,這樣就不會在創建可重用的指令是影響到父作用域的數據。

這個隔離的作用域會有一組來自父作用域的數據組成的對象,該對象名稱前綴為:

@ or @attr : 結合局部作用域屬性的DOM元素上的屬性值。在DOM元素上和指令scope中以string類型綁定。如果沒指定名稱,那么與假定的指令作用域中的名稱相同。

= or =attr:在指令作用域和父作用域之間設置數據的雙向綁定。如果沒指定名稱,那么與假定的指令作用域中的名稱相同。

& or &attr:提供了在父作用域的上下文中執行的表達式。

controller

該指令的控制器構造函數。控制器將會在預編譯之前被實例化,並且可與其他指令共享(參見require屬性)。這實現了指令之間的通信,增強了相互之間的行為。可寫入以下參數:

$scope:與元素相關聯的當前作用域。

$element:當前元素。

$attr:當前元素上的屬性。

$transclude:一個被預綁定到正確的嵌入范圍的linking函數。該范圍可以通過一個第一個可選的參數重寫。function([scope], cloneLinkingFn).

require

需要另外一個指令並將其控制器作為第四個參數注入linking函數。需要以指令的字符串名稱(或字符串數組)注入。如果使用了數組,注入的參數將是一個數組中的相應的順序。如果沒有找到這樣的指令,或如果該指令沒有控制器,那么將會出現一個錯誤。該屬性名稱前綴為:

無前綴:定位當前元素所需的控制器。如果未找到錯誤,拋出一個錯誤。

?:試圖找到所需的控制器,如果沒有找到,則通過空函數。

^:通過搜索元素的父節點來尋找所需的控制器,如果沒找到,拋出個錯誤。

?^:通過搜索元素的父節點來尋找所需的控制器,如果沒有找到,則通過空函數。

controllerAs

指令作用域內的控制器的別名。控制器的別名,以便在該指令模板中引用。該指令需要在適用范圍內定義此配置。當指令被用作組件的情況下有用。

restrict

EACM的子集,限制了對一個特定的指令聲明式的指令。

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

A:元素屬性,<div my-directive="exp"></div>

C:元素類:<div class="my-directive: exp;"></div>

M:注釋,<!-- directive: my-directive exp -->

template

將HTML的內容的指令塊替換當前元素。過程將在新元素上更新之前舊元素的所有屬性/類。該屬性可以指定模板作為一個字符串或一個函數模板,采用兩個參數tElement和tAttrs,並返回一個表示模板的字符串值。

templateUrl

與template基本相同,但模板通過指定的url加載。因為模板是異步加載的,所以conplie和link都會被贊同,等待模板加載完。

replace

指定要插入模塊的位置,默認為false。

true:模板將會替換當前元素。

false:模板將會替換當前元素的內容。

transclude

編譯元素的內容並且使其在指令內有效。這使得組件有私人的狀態,並且嵌入的部分包含到父作用域。

true:該指令可嵌入內容。

false:嵌入整個單元包括在較低的優先級定義的任何指令。

compile

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

compile函數分配模板的轉換。由於大多數指令不做模板轉換,所以它不經常使用。需要用到compile去轉換模板的例子有ngRepeat,或者異步加載內容,如ngView。編譯需要如下參數:

tElement:模板元素,該指令已聲明的元素。只有在元素和子元素上進行模板轉換是安全的。

tAttr:模板屬性,在所有的指令compile函數之間共享的屬性。

transclude:一個transclude linking函數-- function(scope, cloneLinkingFn)

備注:如果模板被克隆,那么template實例和link實例是不同的對象。因此,所有在compile函數里克隆的DOM節點做DOM轉換都是不安全的。具體來說,DOM監聽應該在link函數里而不是在compile函數內。

注意:compile函數不能操作指令去遞歸使用自身的模板或compile函數。編譯這些指令將會導致一個無限循環和堆棧溢出錯誤。可以通過手動使用postLink函數強制編譯指令的模板而不是依靠通過template或templateUrl或在compile函數模板手動編譯。

link

如果未定義compile屬性,則使用此屬性。

function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }

link函數負責注冊DOM監聽及更新DOM操作,該函數在模板被克隆后執行。大部分指令邏輯是放在這里面的。

函數參數:

scope:被注冊監聽的指令使用的作用域。

iElement:元素實例,使用該指令的元素。當子元素已被關聯,那么只能在postLink函數內操作是安全的。

iAttrs:屬性實例,在這個元素上聲明的屬性,在所有指令的linking函數內共享。

controller:如果至少有一個指令的元素定義一個控制器,那么是個控制器實例。控制器在所有的指令中被共享,它允許指令使用控制器作為通信通道。

transcludeFn:預綁定到正確的嵌入范圍的一個linking函數。范圍可以通過一個可選的第一個參數重寫--function([scope], cloneLinkingFn)

pre-linking 函數:在關聯子元素之前執行。

Post-linking函數:在關聯子元素之后執行。

$compile用法:

$compile(element,transclude,maxPriority);

element:將要被編譯和插入模板的元素或者HTML字符串。

transclude:指令內有效的函數。Function(angular.Scope,cloneAttachFn=)

maxPriority:只有在指令比給定的優先級低時應用。只影響根元素,不影響子元素。

返回:

一個用於綁定HTML模板到一個作用域的連接函數。

scope:綁定的作用域。

cloneAttachFn=:如果已提供cloneAttachFn=,則連接函數將克隆模板並且調用cloneAttachFn函數允許去在DOM文檔中適當的地方附上克隆的元素。

cloneAttachFn被調用如:

cloneAttachFn(clonedElement,scope);

clonedElement:一個被傳遞到編譯器的被克隆的原始元素。

scope:當前scope和linking函數執行的地方。

調用linking函數返回模板的元素。有另一個原始元素將被傳遞,或者克隆的元素被提供了cloneAttachFn。

在連接視圖之后直到調用一次$digest()才會更新,這是Angular自動完成的。

如果你需要獲得綁定的頁面,有兩個方法完成:

1.如果你不想在傳給編譯器和保持這個參考范圍之前去請求克隆模板和創建DOM元素:var element = $compile(‘<p>{{value}}</p>’)(scope);

2.另一方面,你需要元素被克隆,從原始的例子中的視圖引用將不指向克隆,而是將要被克隆的模板。在這種情況下,你可以通過cloneattachfn訪問克隆:

  var templateElement = angular.element(‘<p>{{value}}</p>’);
  scope = … ;//定義scope
  var clonedElement = $compile(templateElement)(scope,function(clonedElement,scope){
  //在HTML文檔的合適位子添加克隆元素 
})

使用代碼(注意:jQLite的選擇器功能有限,此處引入了jQuery):

  <div ng-app="Demo" ng-controller="testCtrl as ctrl">
        <div id="contianer">a</div>
  </div>
  (function () {
    angular.module("Demo", [])
    .service("compileTest", ["$compile",compileTest])
    .controller("testCtrl", ["$scope","compileTest",testCtrl])
    function compileTest($compile){
       this.add = function (s) {
          var element = angular.element('<span>{{ctrl.words}}</span>');
          angular.element("#contianer").append($compile(element)(s))
       }
    };
    function testCtrl($scope,compileTest){
         var e = angular.element("#contianer");
         this.words = "A";
         compileTest.add($scope);
    };
  }());

$compile.directive.Attributes

一個包含規范的DOM元素屬性並且能在指令的compile和link函數之間共享的對象。這個值反映了當前綁定的狀態{{}}。

方法

$addClass(classVal);

classVal指定元素該添加的css類。如果一個動畫樣式是可執行的,那么觸發該動畫樣式。

$removeClass(classVal);

classVal指定元素該移除的css類。如果一個動畫樣式是可執行的,那么觸發該動畫樣式。

$updateClass(newClasses,oldClasses);

更新指定元素的css類。newClasses是新的css類,將要替換oldClasses(已存在樣式)。

$observe(key,fn);

觀察一個在內部插入的屬性。當下一次$digest只會,此觀察函數將會被執行一次。當內部插入的值發生變化的時候調用一次此函數。

$set(name,value);

設置DOM元素屬性值。name:屬性名,value:屬性值。

屬性:

$attr;

元素屬性。

使用代碼:

  .red{color:red}
  .blue{color:blue}
  <div ng-app="Demo" ng-controller="testCtrl as ctrl">
      <div class="blue testClass" data-value="value" new-dir>11111</div>
  </div>
  (function () {
    angular.module("Demo", [])
    .directive("newDir",newDir)
    .controller("testCtrl",angular.noop)
    function newDir(){
      return{
        restrict:"ACEM",
        link:function(scope,element,attrs){
           attrs.$addClass("red");
           attrs.$removeClass("blue");
           attrs.$updateClass("newClass","testClass");
           element.bind("mouseover",function(){
               attrs.$set("data-text","hello");
           });
           var count = 0;
           attrs.$observe("data-text",function(){
               console.log(count++);
           });
          }
       }
    };
  }());

對於指令這塊,真的需要多寫多用才能真正的理解這些內容及用法... 這篇關於指令的總結文章沒多少直接展示的使用代碼,不過每個屬性都測試過了用法,也寫過一些自己項目用的或者自己寫着玩的插件... 好了,暫時寫到這,接下來去學習另外的服務...


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM