angular中的compile和link函數


angular中的compile和link函數

前言

這篇文章,我們將通過一個實例來了解 Angular 的 directives (指令)是如何處理的。Angular 是如何在 HTML 中找到這些 directive 的。以及如何編寫自定義的指令。

這是原文提供的代碼:http://www.angularjshub.com/examples/customdirectives/compilelinkfunctions/#top

更加友好的排版:http://blog.wangtuyao.com/post/2014/7/2/compile-and-link-function-in-angular

Initialization, compilation 和 linking 階段

當 Angular 從 HTML 中解析 directive 的時候,我們每個 directive 的解析可以大致分為 3 步。

initialization :這個階段是 Angular 在 DOM 遍歷的時候第一次發現 directive 的時候執行。(它只會發生一次,即便是在 DOM 中出現了多次)initialization 允許 directive 做一些初始化的工作。
compilation :這個階段 Angular 遍歷 DOM ,並且收集所有的 directive 。所有 directive 都可以在這個階段操作 DOM(在這個階段,同一個 directive 在不同的 DOM 節點如果中出現了多次,compilation 將會執行多次)這個階段 scope 是還沒有被附加上去,所以是無法訪問到 scope 的。

linking :這個階段的 DOM 節點,Angular 會把所有 directive 的事件監聽器(event listeners)注冊到 DOM 中,建立對 scope 的監聽。並且把一個 scope 附加到 directive 中。這整個階段是在 compilation 之后進行的。如果想要訪問 scope 就可以在這個階段進行。

創建自定義 directive

我們可以通過 Angular 中提供的 directive 創建自定義的指令。

directive(name, directiveFactory);

方法提供了兩個參數,第一個為 directive 的名字,第二個是一個工廠方法。

在 HTML 中指定來引用一個指令有多種方法,查看更多內容。directive 的命名說有幾種約定的規則:

使用駝峰式命名,首字母小寫。
HTML 中使用小寫字符加橫線的方式。
例如:directive 的名稱為 myCustomDir 所有 HTML 中引用為 my-custom-dir;

上文講了 initialization, compilation 和 linking 階段,現在來看一下 directive 是如何執行的。這是一個最為復雜的例子,這個例子包含了 directive 處理的所有階段。

myModule.directive("myCustomDir", function ()
  {
    // Initialization
    // ...
    // Definition object
    return {
      // Compile function
      compile: function (element, attrs)
      {
        // ...
        return {
          // Pre-link function
          pre: function (scope, element, attrs)
          {
            // ...
          },
          // Post-link function
          post: function (scope, element, attrs)
          {
            // ...
          }
        };
      }
    };
  });

第一個參數表示 directive 的名字,第二個參數表示 directive 的工廠方法,返回一個對象。這個對象可以包含多個屬性,查看更多。這里我們只關注compile 屬性。compile 對應了一個 compile function ,這個方法返回一個包含 pre 和 post 屬性的對象。為了能更好的理解情況下圖:

假如我們定義了如下的自定義的 directive dir1,dir2,dir3 和 dir4 ,Angular 又是如何處理的呢

<... dir1 ...>
  <... dir2 dir3 ...>
    <... dir4 dir1 ...>
    </...>
  </...>
</...>

initialization 和 compilation 階段將會在第一次遍歷 DOM 的時候發生。這里是處理過程:

DOM 第一次遍歷的時候,dir1 將會首先被發現。所以 dir1 的 initialization 將會首先執行,緊接着的是 dir1 的 compilation 。
在第一個 DOM node 處理完后,接着會處理這個 node 的子 node,這里就是 dir2 , dir3;dir2 和 dir3 是 DOM 第一次遍歷到,所以initialization 部分和 compilation 部分會依次調用;
接下來處理 dir2 和 dir3 的子 node,這里包含了 dir4 和 dir1 。因為 dir1 在第一步的時候已經 initialization 過,所以不會再次執行,但是 dir4 還是會調用 initialization 因為它是 DOM 第一次遍歷到。所有的 directive 都會在 initialization 后調用 compilation。
如果 html 中還有其他的 DOM 樹,處理過程也是類似的,以同樣的方式從上到下遍歷 DOM 樹,如果有 DOM 節點存在着子節點,每個字節點都將會被依次遍歷后再進入下一個節點。這個過程結束之后,initialization 和 compilation 的處理也結束了。directive 將會被替換,最終版本的 DOM 樹將會呈現出來。

接下來的處理過程是 linking。這個過程有可以細分為 pre-linking 和 post-linking。由於 Angular 使用 depth-first 方式遍歷 DOM 樹。所以 angular 首先訪問到的是 pre-linking ,當所有 pre-linking 執行完畢后,DOM 遍歷進入到一個“回溯”(backtracking )階段的時候所有的 post-linking 才執行。在 pre-linking 函數做DOM 的轉換時不安全的(pre-linking 的執行是在節點的子節點還未執行link,意思是:在某一個節點執行 pre-linking 的時候,這個節點的子節點還未進行 link),在post-linking 階段才是安全的。

上文,我們已經知道了一種方式在一個模塊中創建一個指令,並且這是一種最復雜的方式,因為我們既需要訪問 compile ,pre-link,和 post-link,但是通常情況下我們不需要全部,我們可以根據需求選擇不同的函數。我們開看一下幾種方式(代碼請看上面的鏈接):

1.不定義對象

我們可以簡單的 Post-link 后,不定義對象返回。這是在例 1. 所示。

myModule.directive("myCustomDir", function ()
{
  // Initialization
  // ...
  // Post-link function
  return function (scope, element, attrs)
  {
    // ...
  };
});

2.定義一個對象和只定義一個 post-link 函數

定義對象后返回 post-link 函數,如例 2.所示:

myModule.directive("myCustomDir", function ()
{
  // Initialization
  // ...
  // Definition object
  return {
    // Post-link function
    link: function (scope, element, attrs)
    {
      // ...
    }
  };
});

3.定義一個對象和定義 post-klink 和 pre-link

返回一個對象,這個對象有一個 link屬性,這個 link 屬性又包含一個 pre 和 post 屬性的對象,分別對應 pre-link 函數和 post-link 函數。如實例 3.所示:

myModule.directive("myCustomDir", function ()
{
  // Initialization
  // ...

  // Definition object
  return {
    link: {
      // Pre-link function
      pre: function (scope, element, attrs)
      {
        // ...
      },
      // Post-link function
      post: function (scope, element, attrs)
      {
        // ...
      }
    }
  };
});

4.定義一個對象和只定義 compile 函數

返回一個對象,包含 compile 屬性,對應 compile 函數,如例 4. 所示:

myModule.directive("myCustomDir", function ()
{
  // Initialization
  // ...

  // Definition object
  return {
    // Compile function
    compile: function (element, attrs)
    {
      // ...
    }
  };
});

5.定義一個對象和定義 compile 和 post-link 函數

myModule.directive("myCustomDir", function ()
{
  // Initialization
  // ...

  // Definition object
  return {
    // Compile function
    compile: function (element, attrs)
    {
      // ...

      // Post-link function
      return function (scope, element, attrs)
      {
        // ...
      };
    }
  };
});

6.定義一個對象和 compile,pre-link 和 post-link 函數

最后一種方式,如例 6.所示

myModule.directive("myCustomDir", function ()
{
  // Initialization
  // ...

  // Definition object
  return {
    // Compile function
    compile: function (element, attrs)
    {
      // ...

      return {
        // Pre-link function
        pre: function (scope, element, attrs)
        {
          // ...
        },
        // Post-link function
        post: function (scope, element, attrs)
        {
          // ...
        }
      };
    }
  };
});

前一篇博文中的代碼作為一個完整的directive示例:

angular.module('demo', [])

.controller('demoController',['$scope',function($scpoe){
$scpoe.rating=52;

}])

.directive('rnStepper', function() {
    return {
        restrict: 'AE',
        require:'ngModel',/*使用屬性模式調用,依賴了ngModel指令*/
        scope:{},
        template: '<button ng-click="decrement()">-</button>' +
                  '<div></div>' +
                  '<button ng-click="increment()">+</button>',
        //link函數可以接受require指令的controller,ngModelController
        link:function(scope,element,attrs,ngModelController){
        
          //利用ngModel指令的controller我們可以利用他的方法很多事情
          ngModelController.$render=function(){
                element.find('div').text(ngModelController.$viewValue);
          };
          function updateModel(offset){
            ngModelController.$setViewValue(ngModelController.$viewValue+offset);
            ngModelController.$render();
          };
          scope.decrement=function(){
            updateModel(-1);
          };
          scope.increment=function(){
            updateModel(1);
          };
        }
    };
});

我們已經了解了在自定義指令中定義 compile 和 link 函數。現在來看一下 compile 和 link 的參數。

Angular 使用了一種內置的、輕量級的 jQuery,稱為 jqLite 來操作和查詢 DOM。但如果頁面中同時有 jQuery 存在,那么 Angular 會用 jQuery 替換掉內置的 jqLite。

compile 函數的簽名如下:

function (element, attrs)

element : 表示被編譯后的、包含了 DOM 節點的 jqLite\jQuery 對象(如:如果 directive 是一個 div 元素,那么 element 是一個 jqLite\jQuery 包裝的對象)。
attrs: attrs 是一個對象。這個對象的每一個屬性代表着 Node 中的每一個同名屬性。(如:Node 中有一個 my-attribute 屬性,那么就可以通過 attrs.myAttribute 來獲取該屬性的值)
link 函數的簽名如下:

function (scope, element, attrs)

scope: directive 的 scope;
element: 和 compile 函數的 element 參數相同;
attrs: 和 compile 函數中的 attrs 參數相同

Reference

http://www.angularjshub.com/examples/customdirectives/compilelinkfunctions/
http://www.cnblogs.com/wangtuyao/p/compile-and-link-function-in-angular.html
http://blog.wangtuyao.com


免責聲明!

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



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