AngularJS自訂控制項執行個體詳解_AngularJS

來源:互聯網
上載者:User

本文執行個體講述了AngularJS自訂控制項。分享給大家供大家參考,具體如下:

自訂指令介紹

AngularJS 指令作用是在 AngulaJS 應用中操作 Html 渲染。比如說,插入指令 ( {{ }} ), ng-repeat 指令以及 ng-if 指令。

當然你也可以實現自己的。這就是 AngularJS 所謂的"教會 HTML 玩新姿勢”。本文將告訴你如何做到。

指令類型

可以自訂的指令類型如下:

元素
屬性
CSS class
Comment directives

這裡面,AngularJS 強烈建議你用元素和屬性類型,而不用 CSS class 和 comment directives (除非迫不得已)。

指令類型決定了指令何時被啟用。當 AngularJS 在 HTML 範本中找到一個 HTML 元素的時候,元素指令被啟用。當 AngularJS 找到一個 HTML 元素屬性時,屬性指令被啟用。當 AngularJS 找到一個 CSS class 時, CSS class 指令被啟用,最後,當 AngularJS 找到 HTML comment 時,comment directive 被啟用。

基礎例子

你可以向模組註冊一個指令,像這樣:

<!-- lang: js -->myapp = angular.module("myapp", []);myapp.directive('div', function() {  var directive = {};  directive.restrict = 'E'; /* restrict this directive to elements */  directive.template = "My first directive: {{textToInsert}}";  return directive;});

來看模組中調用的 directive() 函數。當你調用該函數時,意味著你要註冊一個新指令。directive() 函數的第一個參數是新註冊指令的名稱。這是之後你在 HTML 範本中調用它的時候用的名稱。例子中,我用了 'div' ,意思是說當 HTML 範本每次在找到 HTML 元素類型是 div 的時候,這個指令都會被啟用。

傳遞給 directive 函數的第二個參數是一個工廠函數。調用該函數會返回一個指令的定義。 AngularJS 通過調用該函數來擷取包含指令定義的 JavaScript 對象。如果你有仔細看上面的例子,你會知道它返回的確是是一個 Javascript 對象。

這個從工廠函數中返回的 Javascript 對象有兩個屬性: restrict 和 template 欄位。

restrict 欄位用來設定指令何時被啟用,是匹配到 HTML 元素時,還是匹配到元素屬性時。也就是指令類型。把restrict 設定為 E 時,只有名為 div 的 HTML 元素才會啟用該指令。把 restrict 設定為 A 時,只有名為 div 的 HTML 元素屬性才會啟用該指令。你也可以用 AE ,這樣會使得匹配到元素名和元素屬性名稱時,都可以啟用該指令。

template 欄位是一個 HTML 範本,用來替換匹配的 div 元素。它會把匹配到的 div 當成一個預留位置,然後用 HTML 範本在同一位置來替換掉它。也就是說,HTML 範本替換匹配的到 HTML 元素。

比如說你的 HTML 頁面有這樣一段 HTML:

<!-- lang: js --><div ng-controller="MyController" >  <div>This div will be replaced</div></div>

當 AngularJS 找到內嵌的 div 的時候,會啟用自訂的指令。然後把該 div 元素替換掉,替換後的 HTML 將變成這樣:

<!-- lang: js -->My first directive: {{textToInsert}}

然後你看到了,這段 HTML 裡麵包含了一個插入指令 ({{textToInsert}})。AngularJS 會再執行一次,讓插入指令實際值顯示出來。然後 $scope.textToInsert 屬性將會在這個 HTML 點上替換掉插入指令預留位置。

template 和 templateUrl 屬性

建立自訂指令最簡單的例子就是上面這樣了。你的指令用來產生 HTML,然後你把 HTML 放到指令定義對象的 template 屬性中。下面這個例子是上面那最簡單的例子的重複,注意 template部分:

<!-- lang: js -->myapp = angular.module("myapp", []);myapp.directive('div', function() {  var directive = {};  directive.restrict = 'E'; /* restrict this directive to elements */  directive.template = "My first directive: {{textToInsert}}";  return directive;});

如果 HTML 範本越來越大,把它寫在一個 Javascript 字串中肯定非常難維護。你可以把它放到一個單獨的檔案中,然後 AngularJS 可以通過這個檔案把它載入進來。然後再把 HTML 範本檔案的 URL 放到指令定義對象的 templateUrl 屬性中。下面是例子:

<!-- lang: js -->myapp = angular.module("myapp", []);myapp.directive('div', function() {  var directive = {};  directive.restrict = 'E'; /* restrict this directive to elements */  directive.templateUrl = "/myapp/html-templates/div-template.html";  return directive;});

然後 AngularJS 會從 templateUrl 屬性中設定的 URL 將 HTML 範本載入進來。

用獨立的 HTML 範本檔案,設定 templateUrl 屬性,在你要建立更多的通用指令時會顯得更加有用,比如說用來顯示使用者資訊的指令。例子:

<!-- lang: js -->myapp = angular.module("myapp", []);myapp.directive('userinfo', function() {  var directive = {};  directive.restrict = 'E'; /* restrict this directive to elements */  directive.templateUrl = "/myapp/html-templates/userinfo-template.html";  return directive;});

例子建立了一個指令,當AngularJS 找到一個 <userinfo> 元素的時候就會啟用它。AngularJS 載入指向 /myapp/html-templates/userinfo-template.html 的模板並解析它,它就像從一開始就被嵌在上一級 HTML 檔案中一樣。

從指令中隔離 $scope

在上面的例子中,把 userinfo 指令硬綁定到了 $scope ,因為 HTML 範本是直接引用 textToInsert 屬性的。直接引用 $scope 讓指令在同一個 controller 中的時候,非常難複用,因為 $scope在同一個 controller 中的值都是一樣的。比如說,你在頁面的 HTML 中這樣寫的時候:

<!-- lang: js --><userinfo></userinfo><userinfo></userinfo>

這兩個 <userinfo> 元素會被同樣的 HTML 範本取代,然後綁定到同樣的 $scope 變數上。結果就是兩個 <userinfo> 元素將會被一模一樣的 HTML 程式碼給替換了。

為了把兩個 <userinfo> 元素繫結到 $scope 中的不同的值,你需要給 HTML 範本一個 isolate scope。

所謂 isolate scope 是綁定在指令上的獨立的 scope 對象。下面是定義的例子:

<!-- lang: js -->myapp.directive('userinfo', function() {  var directive = {};  directive.restrict = 'E';  directive.template = "User : {{user.firstName}} {{user.lastName}}";  directive.scope = {    user : "=user"  }  return directive;})

請看 HTMl 模板中的兩個插入指令 {{user.firstName}} 和 {{user.lastName}}。注意 user. 部分。以及directive.scope 屬性。 directive.scope 是個帶 user 屬性的 Javascript 對象。於是directive.scope 就成為了 isolate scope 對象,現在 HTML 範本被綁到了 directive.scope.user 上(通過 {{user.firstName}} 和 {{user.lastName}} 插入指令)。

directive.scope.user 被設定為 “=user” 。意思是說 directive.scope.user 和 scope 中的屬性綁在一起的(不是 isolate scope),scope 中的屬性通過 user 屬性傳入 <userinfo> 元素。聽起來挺費勁的,直接看例子:

<!-- lang: js --><userinfo user="jakob"></userinfo><userinfo user="john"></userinfo>

這兩個 <userinfo> 元素都有 user 屬性。user 的值指向了 $scope 中同名屬性,被指定的 $scope中的屬性將在 userinfo 的 isolate scope object 中被使用。

下面是完整的例子:

<!-- lang: js --><userinfo user="jakob"></userinfo><userinfo user="john"></userinfo><script>myapp.directive('userinfo', function() {  var directive = {};  directive.restrict = 'E';  directive.template = "User : <b>{{user.firstName}}</b> <b>{{user.lastName}}</b>";  directive.scope = {    user : "=user"  }  return directive;});myapp.controller("MyController", function($scope, $http) {  $scope.jakob = {};  $scope.jakob.firstName = "Jakob";  $scope.jakob.lastName = "Jenkov";  $scope.john = {};  $scope.john.firstName = "John";  $scope.john.lastName = "Doe";});</script>

compile() 和 link() 函數

如果你需要在你的指令中做更進階的操作,單純使用 HTML 範本做不到的時候,你可以考慮使用compile() 和 link() 函數。

compile() 和 link() 函數定義了指令如何修改匹配到的 HTML。

當指令第一次被 AngularJS 編譯的時候 (在 HTML 中第一次被發現),compile() 函數被調用。compile() 函數將為該元素做一次性配置。

然後 compile() 函數以返回 link() 作為終結。link() 函數將在每次元素被綁到 $scope 資料時,都被調用。

下面是例子:

<!-- lang: js --><script>myapp = angular.module("myapp", []);myapp.directive('userinfo', function() {  var directive = {};  directive.restrict = 'E'; /* restrict this directive to elements */  directive.compile = function(element, attributes) {    // do one-time configuration of element.    var linkFunction = function($scope, element, atttributes) {      // bind element to data in $scope    }    return linkFunction;  }  return directive;});</script>

compile() 函數帶兩個參數: element 和 attributes。

element 參數是 jqLite 封裝過的 DOM 元素。AngularJS 有一個簡化版 jQuery 可以用於操作 DOM,所以對 element 的 DOM 操作方式和你在 jQuery 中所知的一樣。

attributes 參數是包含了 DOM 元素中的所有的屬性集合的 Javascript 對象。因此,你要訪問某個屬性你可以這樣寫 attributes.type。

link() 函數帶三個參數: $scope,element 和 attributes。element 和attributes 參數和傳到 compile() 函數中的一樣。$scope 參數是正常的 scope 對象,或者當你在指令定義對象中定義了 isolate scope 的時候,它就是 isolate scope。

compile() 和 link() 的命名相當混亂。它們肯定是受編譯隊伍的啟發而命名的。我可以看到相似點,不過一個編譯器是傳入一次,然後輸出。而指令則是配置一次 HTML 元素,然後在之後的 $scope 對象改變時進行更新。

compile() 函數如果叫做 create(), init() 或者 configure()也許會更好。這樣可以表達出這個函數只會被調用一次的意思。

而 link() 函數如果叫 bind() 或者 render() 也許會更好,能更好的表達出這樣的意思,在指令綁定資料或者重綁定資料的時候,這個函數將會被調用。

下面是一個完整的例子,示範了指令使用 compile() 和 link() 函數的:

<!-- lang: js --><div ng-controller="MyController" >  <userinfo >This will be replaced</userinfo></div><script>  myapp = angular.module("myapp", []);  myapp.directive('userinfo', function() {    var directive = {};    directive.restrict = 'E'; /* restrict this directive to elements */    directive.compile = function(element, attributes) {      element.css("border", "1px solid #cccccc");      var linkFunction = function($scope, element, attributes) {        element.html("This is the new content: " + $scope.firstName);        element.css("background-color", "#ffff00");      }      return linkFunction;    }    return directive;  })  myapp.controller("MyController", function($scope, $http) {    $scope.cssClass = "notificationDiv";    $scope.firstName = "Jakob";    $scope.doClick = function() {      console.log("doClick() called");    }  });</script>

compile() 函數設定 HTML 元素的 border 。它只執行一次,因為 compile() 函數只執行一次。

link() 替換 HTML 元素內容,並且把背景顏色設定為黃色。

把設定 border 放在 compile(), 把背景顏色放在 link() 沒啥特別的理由。你可以把所有的操作都丟到 compile(),或者都放到 link()。如果都放到 compile() 它們只會被設定一次(你需要它們是常量的話)。如果放到了 link(),它們會在每次 HTML 元素被綁到 $scope 的時候都發生變化。當你希望根據 $scope 中的資料來設定 boarder 和背景顏色的時候這非常有用。

只設定 link() 函數

有時候你的指令可能不需要 compile() 。你只需要用到 link()。這種情況下,你可以直接設定指令定義對象中的 link() 函數。下面是一個對上面例子的修改,只用 link 函數:

<!-- lang: js --><div ng-controller="MyController" >  <userinfo >This will be replaced</userinfo></div><script>  myapp = angular.module("myapp", []);  myapp.directive('userinfo', function() {    var directive = {};    directive.restrict = 'E'; /* restrict this directive to elements */    directive.link = function($scope, element, attributes) {        element.html("This is the new content: " + $scope.firstName);        element.css("background-color", "#ffff00");    }    return directive;  })  myapp.controller("MyController", function($scope, $http) {    $scope.cssClass = "notificationDiv";    $scope.firstName = "Jakob";    $scope.doClick = function() {      console.log("doClick() called");    }  });</script>

注意 link() 方法和之前例子中返回的 link() 是完全一樣的。

通過 Transclusion 封裝元素的指令

到目前為止,我們看到的所有例子,都是把匹配到的元素內容給替換為指令的指定內容,不管是通過 Javascript 也好或者 HTML 範本也好。不過如果遇到內容中有部分是開發人員可以指定的內容的時候呢?比如說:

<!-- lang: js --><mytransclude>This is a transcluded directive {{firstName}}</mytransclude>

標記為 <mytransclude> 的元素,它的部分內容可以由開發人員設定。因此,這部分 HTML 不應該被指令的 HTML 範本給替換。我們實際上是希望這部分 HTML 由 AngularJS 來處理的。這個處理叫做 “transclusion”。 1

為了能讓 AngularJS 把這部分 HTML 放到指令內部處理,你必須設定指令定義對象的 transclude 屬性為 true。你還需要告訴 AngularJS,指令的 HTML 範本中哪一部分需要把 transcluded HTML 包含進來。你可以通過插入 ng-transclude 屬性 (實際上,是一個指令) 到 HTML 範本的 HTML 元素中,標記你想要添加 transcluded HTML 的元素。

下面是一個 AngularJS 指令,示範如何使用 transclusion:

<!-- lang: js --><mytransclude>This is a transcluded directive {{firstName}}</mytransclude><script>  myapp = angular.module("myapp", []);  myapp.directive('mytransclude', function() {    var directive = {};    directive.restrict = 'E'; /* restrict this directive to elements */    directive.transclude = true;    directive.template = "<div class='myTransclude' ng-transclude></div>";    return directive;  });  myapp.controller("MyController", function($scope, $http) {    $scope.firstName = "Jakob";  });</script>

注意 <mytransclude> 元素內的 HTML。這部分 HTML 程式碼包含了插入指令 {{firstName}}。我們希望 AngularJS 來為我們處理這部分 HTML,讓插入指令執行。為了實現這個目的,我在指令定義對象中把 transclude 設定為 true。我還在 HTML 範本中用到了 ng-transclude 屬性。這個屬性告訴 AngularJS 什麼元素需要插入到 transcluded HTML。

1: 說實話,我沒看懂那個定義,說的太TM難懂了,而且我好不爽寫代碼沒輸出的教程。只好自己動手做做例子。我覺得其實應該是這樣的,把目標元素內容作為一個整體,拿到 HTML 範本中來,添加到 ng-transclude 指定的標籤下。這個處理,我覺得應該就是叫做 transcluded。比如說剛才的例子(有些 bug,自己修正一下),因為 directive.transclude = true; ,所以原來 <mytransclude> 元素內的 HTML:

<!-- lang: js -->This is a transcluded directive {{firstName}}

在啟用指令 'mytransclude' 的時候,會被拿到 'mytransclude' 指令的模板中來,放到被 ng-transclude 指定的

<!-- lang: js -->"<div class='myTransclude' ng-transclude></div>"

中。於是最終輸出的結果應該是:

<!-- lang: js --><mytransclude>  <div class='myTransclude' ng-transclude>    <span class="ng-scope ng-binding">This is a transcluded directive Jakob</span>  </div></mytransclude>

更多關於AngularJS相關內容感興趣的讀者可查看本站專題:《AngularJS入門與進階教程》及《AngularJS MVC架構總結》

希望本文所述對大家AngularJS程式設計有所協助。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.