AngularJS Custom Directivescss
好討厭不帶日期的博客,並且說得好囉嗦html
#自定義指令介紹angularjs
AngularJS 指令做用是在 AngulaJS 應用中操做 Html 渲染。好比說,內插指令 ( {{ }}
), ng-repeat
指令以及 ng-if
指令。app
固然你也能夠實現本身的。這就是 AngularJS 所謂的"教會 HTML 玩新姿式"。本文將告訴你如何作到。函數
#指令類型this
能夠自定義的指令類型以下:spa
這裏面,AngularJS 強烈建議你用元素和屬性類型,而不用 CSS class 和 comment directives (除非無可奈何)。翻譯
指令類型決定了指令什麼時候被激活。當 AngularJS 在 HTML 模板中找到一個 HTML 元素的時候,元素指令被激活。當 AngularJS 找到一個 HTML 元素屬性時,屬性指令被激活。當 AngularJS 找到一個 CSS class 時, CSS class 指令被激活,最後,當 AngularJS 找到 HTML comment 時,comment directive 被激活。rest
#基礎例子code
你能夠向模塊註冊一個指令,像這樣:
<!-- 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>
而後我又回去把翻譯改了。如今感受念起來順口一點了。