AngularJS自定義控件實例詳解
本文實例講述了AngularJS自定義控件。分享給大家供大家參考,具體如下:
自定義指令介紹
AngularJS 指令作用是在 AngulaJS 應(yīng)用中操作 Html 渲染。比如說,內(nèi)插指令 ( {{ }} ), ng-repeat 指令以及 ng-if 指令。
當(dāng)然你也可以實現(xiàn)自己的。這就是 AngularJS 所謂的"教會 HTML 玩新姿勢”。本文將告訴你如何做到。
指令類型
可以自定義的指令類型如下:
元素
屬性
CSS class
Comment directives
這里面,AngularJS 強(qiáng)烈建議你用元素和屬性類型,而不用 CSS class 和 comment directives (除非迫不得已)。
指令類型決定了指令何時被激活。當(dāng) AngularJS 在 HTML 模板中找到一個 HTML 元素的時候,元素指令被激活。當(dāng) AngularJS 找到一個 HTML 元素屬性時,屬性指令被激活。當(dāng) AngularJS 找到一個 CSS class 時, CSS class 指令被激活,最后,當(dāng) AngularJS 找到 HTML comment 時,comment directive 被激活。
基礎(chǔ)例子
你可以向模塊注冊一個指令,像這樣:
<!-- 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; });
來看模塊中調(diào)用的 directive() 函數(shù)。當(dāng)你調(diào)用該函數(shù)時,意味著你要注冊一個新指令。directive() 函數(shù)的第一個參數(shù)是新注冊指令的名稱。這是之后你在 HTML 模板中調(diào)用它的時候用的名稱。例子中,我用了 'div' ,意思是說當(dāng) HTML 模板每次在找到 HTML 元素類型是 div 的時候,這個指令都會被激活。
傳遞給 directive 函數(shù)的第二個參數(shù)是一個工廠函數(shù)。調(diào)用該函數(shù)會返回一個指令的定義。 AngularJS 通過調(diào)用該函數(shù)來獲取包含指令定義的 JavaScript 對象。如果你有仔細(xì)看上面的例子,你會知道它返回的確是是一個 Javascript 對象。
這個從工廠函數(shù)中返回的 Javascript 對象有兩個屬性: restrict 和 template 字段。
restrict 字段用來設(shè)置指令何時被激活,是匹配到 HTML 元素時,還是匹配到元素屬性時。也就是指令類型。把restrict 設(shè)置為 E 時,只有名為 div 的 HTML 元素才會激活該指令。把 restrict 設(shè)置為 A 時,只有名為 div 的 HTML 元素屬性才會激活該指令。你也可以用 AE ,這樣會使得匹配到元素名和元素屬性名時,都可以激活該指令。
template 字段是一個 HTML 模板,用來替換匹配的 div 元素。它會把匹配到的 div 當(dāng)成一個占位符,然后用 HTML 模板在同一位置來替換掉它。也就是說,HTML 模板替換匹配的到 HTML 元素。
比如說你的 HTML 頁面有這樣一段 HTML:
<!-- lang: js --> <div ng-controller="MyController" > <div>This div will be replaced</div> </div>
當(dāng) AngularJS 找到內(nèi)嵌的 div 的時候,會激活自定義的指令。然后把該 div 元素替換掉,替換后的 HTML 將變成這樣:
<!-- lang: js --> My first directive: {{textToInsert}}
然后你看到了,這段 HTML 里面包含了一個內(nèi)插指令 ({{textToInsert}})。AngularJS 會再執(zhí)行一次,讓內(nèi)插指令實際值顯示出來。然后 $scope.textToInsert 屬性將會在這個 HTML 點上替換掉內(nèi)插指令占位符。
template 和 templateUrl 屬性
創(chuàng)建自定義指令最簡單的例子就是上面這樣了。你的指令用來生成 HTML,然后你把 HTML 放到指令定義對象的 template 屬性中。下面這個例子是上面那最簡單的例子的重復(fù),注意 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 字符串中肯定非常難維護(hù)。你可以把它放到一個單獨(dú)的文件中,然后 AngularJS 可以通過這個文件把它加載進(jìn)來。然后再把 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 屬性中設(shè)置的 URL 將 HTML 模板加載進(jìn)來。
用獨(dú)立的 HTML 模板文件,設(shè)置 templateUrl 屬性,在你要創(chuàng)建更多的通用指令時會顯得更加有用,比如說用來顯示用戶信息的指令。例子:
<!-- 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; });
例子創(chuàng)建了一個指令,當(dāng)AngularJS 找到一個 <userinfo> 元素的時候就會激活它。AngularJS 加載指向 /myapp/html-templates/userinfo-template.html 的模板并解析它,它就像從一開始就被嵌在上一級 HTML 文件中一樣。
從指令中隔離 $scope
在上面的例子中,把 userinfo 指令硬綁定到了 $scope ,因為 HTML 模板是直接引用 textToInsert 屬性的。直接引用 $scope 讓指令在同一個 controller 中的時候,非常難復(fù)用,因為 $scope在同一個 controller 中的值都是一樣的。比如說,你在頁面的 HTML 中這樣寫的時候:
<!-- lang: js --> <userinfo></userinfo> <userinfo></userinfo>
這兩個 <userinfo> 元素會被同樣的 HTML 模板取代,然后綁定到同樣的 $scope 變量上。結(jié)果就是兩個 <userinfo> 元素將會被一模一樣的 HTML 代碼給替換了。
為了把兩個 <userinfo> 元素綁定到 $scope 中的不同的值,你需要給 HTML 模板一個 isolate scope。
所謂 isolate scope 是綁定在指令上的獨(dú)立的 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 模板中的兩個內(nèi)插指令 {{user.firstName}} 和 {{user.lastName}}。注意 user. 部分。以及directive.scope 屬性。 directive.scope 是個帶 user 屬性的 Javascript 對象。于是directive.scope 就成為了 isolate scope 對象,現(xiàn)在 HTML 模板被綁到了 directive.scope.user 上(通過 {{user.firstName}} 和 {{user.lastName}} 內(nèi)插指令)。
directive.scope.user 被設(shè)置為 “=user” 。意思是說 directive.scope.user 和 scope 中的屬性綁在一起的(不是 isolate scope),scope 中的屬性通過 user 屬性傳入 <userinfo> 元素。聽起來挺費(fèi)勁的,直接看例子:
<!-- 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() 函數(shù)
如果你需要在你的指令中做更高級的操作,單純使用 HTML 模板做不到的時候,你可以考慮使用compile() 和 link() 函數(shù)。
compile() 和 link() 函數(shù)定義了指令如何修改匹配到的 HTML。
當(dāng)指令第一次被 AngularJS 編譯的時候 (在 HTML 中第一次被發(fā)現(xiàn)),compile() 函數(shù)被調(diào)用。compile() 函數(shù)將為該元素做一次性配置。
然后 compile() 函數(shù)以返回 link() 作為終結(jié)。link() 函數(shù)將在每次元素被綁到 $scope 數(shù)據(jù)時,都被調(diào)用。
下面是例子:
<!-- 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() 函數(shù)帶兩個參數(shù): element 和 attributes。
element 參數(shù)是 jqLite 包裝過的 DOM 元素。AngularJS 有一個簡化版 jQuery 可以用于操作 DOM,所以對 element 的 DOM 操作方式和你在 jQuery 中所知的一樣。
attributes 參數(shù)是包含了 DOM 元素中的所有的屬性集合的 Javascript 對象。因此,你要訪問某個屬性你可以這樣寫 attributes.type。
link() 函數(shù)帶三個參數(shù): $scope,element 和 attributes。element 和attributes 參數(shù)和傳到 compile() 函數(shù)中的一樣。$scope 參數(shù)是正常的 scope 對象,或者當(dāng)你在指令定義對象中定義了 isolate scope 的時候,它就是 isolate scope。
compile() 和 link() 的命名相當(dāng)混亂。它們肯定是受編譯隊伍的啟發(fā)而命名的。我可以看到相似點,不過一個編譯器是傳入一次,然后輸出。而指令則是配置一次 HTML 元素,然后在之后的 $scope 對象改變時進(jìn)行更新。
compile() 函數(shù)如果叫做 create(), init() 或者 configure()也許會更好。這樣可以表達(dá)出這個函數(shù)只會被調(diào)用一次的意思。
而 link() 函數(shù)如果叫 bind() 或者 render() 也許會更好,能更好的表達(dá)出這樣的意思,在指令綁定數(shù)據(jù)或者重綁定數(shù)據(jù)的時候,這個函數(shù)將會被調(diào)用。
下面是一個完整的例子,演示了指令使用 compile() 和 link() 函數(shù)的:
<!-- 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() 函數(shù)設(shè)置 HTML 元素的 border 。它只執(zhí)行一次,因為 compile() 函數(shù)只執(zhí)行一次。
link() 替換 HTML 元素內(nèi)容,并且把背景顏色設(shè)置為黃色。
把設(shè)置 border 放在 compile(), 把背景顏色放在 link() 沒啥特別的理由。你可以把所有的操作都丟到 compile(),或者都放到 link()。如果都放到 compile() 它們只會被設(shè)置一次(你需要它們是常量的話)。如果放到了 link(),它們會在每次 HTML 元素被綁到 $scope 的時候都發(fā)生變化。當(dāng)你希望根據(jù) $scope 中的數(shù)據(jù)來設(shè)置 boarder 和背景顏色的時候這非常有用。
只設(shè)置 link() 函數(shù)
有時候你的指令可能不需要 compile() 。你只需要用到 link()。這種情況下,你可以直接設(shè)置指令定義對象中的 link() 函數(shù)。下面是一個對上面例子的修改,只用 link 函數(shù):
<!-- 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 封裝元素的指令
到目前為止,我們看到的所有例子,都是把匹配到的元素內(nèi)容給替換為指令的指定內(nèi)容,不管是通過 Javascript 也好或者 HTML 模板也好。不過如果遇到內(nèi)容中有部分是開發(fā)者可以指定的內(nèi)容的時候呢?比如說:
<!-- lang: js --> <mytransclude>This is a transcluded directive {{firstName}}</mytransclude>
標(biāo)記為 <mytransclude> 的元素,它的部分內(nèi)容可以由開發(fā)者設(shè)置。因此,這部分 HTML 不應(yīng)該被指令的 HTML 模板給替換。我們實際上是希望這部分 HTML 由 AngularJS 來處理的。這個處理叫做 “transclusion”。 1
為了能讓 AngularJS 把這部分 HTML 放到指令內(nèi)部處理,你必須設(shè)置指令定義對象的 transclude 屬性為 true。你還需要告訴 AngularJS,指令的 HTML 模板中哪一部分需要把 transcluded HTML 包含進(jìn)來。你可以通過插入 ng-transclude 屬性 (實際上,是一個指令) 到 HTML 模板的 HTML 元素中,標(biāo)記你想要添加 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> 元素內(nèi)的 HTML。這部分 HTML 代碼包含了內(nèi)插指令 {{firstName}}。我們希望 AngularJS 來為我們處理這部分 HTML,讓內(nèi)插指令執(zhí)行。為了實現(xiàn)這個目的,我在指令定義對象中把 transclude 設(shè)置為 true。我還在 HTML 模板中用到了 ng-transclude 屬性。這個屬性告訴 AngularJS 什么元素需要插入到 transcluded HTML。
1: 說實話,我沒看懂那個定義,說的太TM難懂了,而且我好不爽寫代碼沒輸出的教程。只好自己動手做做例子。我覺得其實應(yīng)該是這樣的,把目標(biāo)元素內(nèi)容作為一個整體,拿到 HTML 模板中來,添加到 ng-transclude 指定的標(biāo)簽下。這個處理,我覺得應(yīng)該就是叫做 transcluded。比如說剛才的例子(有些 bug,自己修正一下),因為 directive.transclude = true; ,所以原來 <mytransclude> 元素內(nèi)的 HTML:
<!-- lang: js --> This is a transcluded directive {{firstName}}
在激活指令 'mytransclude' 的時候,會被拿到 'mytransclude' 指令的模板中來,放到被 ng-transclude 指定的
<!-- lang: js --> "<div class='myTransclude' ng-transclude></div>"
中。于是最終輸出的結(jié)果應(yīng)該是:
<!-- lang: js --> <mytransclude> <div class='myTransclude' ng-transclude> <span class="ng-scope ng-binding">This is a transcluded directive Jakob</span> </div> </mytransclude>
更多關(guān)于AngularJS相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《AngularJS入門與進(jìn)階教程》及《AngularJS MVC架構(gòu)總結(jié)》
希望本文所述對大家AngularJS程序設(shè)計有所幫助。
相關(guān)文章
angular.JS實現(xiàn)網(wǎng)頁禁用調(diào)試、復(fù)制和剪切
這篇文章主要給大家介紹了angular.JS實現(xiàn)網(wǎng)頁禁用調(diào)試、復(fù)制和剪切的相關(guān)資料,文中介紹的非常詳細(xì),相信對大家具有一定的參考價值,需要的朋友們下面來一起看看吧。2017-03-03angular json對象push到數(shù)組中的方法
下面小編就為大家分享一篇angular json對象push到數(shù)組中的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-02-02Angular5升級RxJS到5.5.3報錯:EmptyError: no elements in sequence的解
這篇文章主要給大家介紹了關(guān)于Angular5升級RxJS到5.5.3報錯:EmptyError: no elements in sequence的解決方法,文中介紹了兩個解決方法,大家可以選擇使用,需要的朋友可以參考借鑒,下面來一起看看吧。2018-04-04angularjs實現(xiàn)上拉加載和下拉刷新數(shù)據(jù)功能
本篇文章主要介紹了angularjs實現(xiàn)上拉加載和下拉刷新數(shù)據(jù)功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-06-06Angular中$state.go頁面跳轉(zhuǎn)并傳遞參數(shù)的方法
這篇文章主要介紹了angular中$state.go頁面跳轉(zhuǎn)并傳遞參數(shù)的方法,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-05-05Angular利用內(nèi)容投射向組件輸入ngForOf模板的方法
本篇文章主要介紹了Angular利用內(nèi)容投射向組件輸入ngForOf模板的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-03-03Angular.Js中過濾器filter與自定義過濾器filter實例詳解
Angularjs過濾器是 angularjs非常棒的特性之一。有朝一日,你可能需要使用自定義過濾器,所以下面這篇文章主要給大家介紹了Angular.Js中過濾器filter與自定義過濾器filter的相關(guān)資料,需要的朋友可以參考借鑒,下面來一起看看吧。2017-05-05詳解angular筆記路由之a(chǎn)ngular-router
本篇文章主要介紹了詳解angular筆記路由之a(chǎn)ngular-router,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-09-09