AngularJS自定義控件實(shí)例詳解
本文實(shí)例講述了AngularJS自定義控件。分享給大家供大家參考,具體如下:
自定義指令介紹
AngularJS 指令作用是在 AngulaJS 應(yīng)用中操作 Html 渲染。比如說(shuō),內(nèi)插指令 ( {{ }} ), ng-repeat 指令以及 ng-if 指令。
當(dāng)然你也可以實(shí)現(xiàn)自己的。這就是 AngularJS 所謂的"教會(huì) HTML 玩新姿勢(shì)”。本文將告訴你如何做到。
指令類型
可以自定義的指令類型如下:
元素
屬性
CSS class
Comment directives
這里面,AngularJS 強(qiáng)烈建議你用元素和屬性類型,而不用 CSS class 和 comment directives (除非迫不得已)。
指令類型決定了指令何時(shí)被激活。當(dāng) AngularJS 在 HTML 模板中找到一個(gè) HTML 元素的時(shí)候,元素指令被激活。當(dāng) AngularJS 找到一個(gè) HTML 元素屬性時(shí),屬性指令被激活。當(dāng) AngularJS 找到一個(gè) CSS class 時(shí), CSS class 指令被激活,最后,當(dāng) AngularJS 找到 HTML comment 時(shí),comment directive 被激活。
基礎(chǔ)例子
你可以向模塊注冊(cè)一個(gè)指令,像這樣:
<!-- 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;
});
來(lái)看模塊中調(diào)用的 directive() 函數(shù)。當(dāng)你調(diào)用該函數(shù)時(shí),意味著你要注冊(cè)一個(gè)新指令。directive() 函數(shù)的第一個(gè)參數(shù)是新注冊(cè)指令的名稱。這是之后你在 HTML 模板中調(diào)用它的時(shí)候用的名稱。例子中,我用了 'div' ,意思是說(shuō)當(dāng) HTML 模板每次在找到 HTML 元素類型是 div 的時(shí)候,這個(gè)指令都會(huì)被激活。
傳遞給 directive 函數(shù)的第二個(gè)參數(shù)是一個(gè)工廠函數(shù)。調(diào)用該函數(shù)會(huì)返回一個(gè)指令的定義。 AngularJS 通過(guò)調(diào)用該函數(shù)來(lái)獲取包含指令定義的 JavaScript 對(duì)象。如果你有仔細(xì)看上面的例子,你會(huì)知道它返回的確是是一個(gè) Javascript 對(duì)象。
這個(gè)從工廠函數(shù)中返回的 Javascript 對(duì)象有兩個(gè)屬性: restrict 和 template 字段。
restrict 字段用來(lái)設(shè)置指令何時(shí)被激活,是匹配到 HTML 元素時(shí),還是匹配到元素屬性時(shí)。也就是指令類型。把restrict 設(shè)置為 E 時(shí),只有名為 div 的 HTML 元素才會(huì)激活該指令。把 restrict 設(shè)置為 A 時(shí),只有名為 div 的 HTML 元素屬性才會(huì)激活該指令。你也可以用 AE ,這樣會(huì)使得匹配到元素名和元素屬性名時(shí),都可以激活該指令。
template 字段是一個(gè) HTML 模板,用來(lái)替換匹配的 div 元素。它會(huì)把匹配到的 div 當(dāng)成一個(gè)占位符,然后用 HTML 模板在同一位置來(lái)替換掉它。也就是說(shuō),HTML 模板替換匹配的到 HTML 元素。
比如說(shuō)你的 HTML 頁(yè)面有這樣一段 HTML:
<!-- lang: js --> <div ng-controller="MyController" > <div>This div will be replaced</div> </div>
當(dāng) AngularJS 找到內(nèi)嵌的 div 的時(shí)候,會(huì)激活自定義的指令。然后把該 div 元素替換掉,替換后的 HTML 將變成這樣:
<!-- lang: js -->
My first directive: {{textToInsert}}
然后你看到了,這段 HTML 里面包含了一個(gè)內(nèi)插指令 ({{textToInsert}})。AngularJS 會(huì)再執(zhí)行一次,讓內(nèi)插指令實(shí)際值顯示出來(lái)。然后 $scope.textToInsert 屬性將會(huì)在這個(gè) HTML 點(diǎn)上替換掉內(nèi)插指令占位符。
template 和 templateUrl 屬性
創(chuàng)建自定義指令最簡(jiǎn)單的例子就是上面這樣了。你的指令用來(lái)生成 HTML,然后你把 HTML 放到指令定義對(duì)象的 template 屬性中。下面這個(gè)例子是上面那最簡(jiǎn)單的例子的重復(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 模板越來(lái)越大,把它寫在一個(gè) Javascript 字符串中肯定非常難維護(hù)。你可以把它放到一個(gè)單獨(dú)的文件中,然后 AngularJS 可以通過(guò)這個(gè)文件把它加載進(jìn)來(lái)。然后再把 HTML 模板文件的 URL 放到指令定義對(duì)象的 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 會(huì)從 templateUrl 屬性中設(shè)置的 URL 將 HTML 模板加載進(jìn)來(lái)。
用獨(dú)立的 HTML 模板文件,設(shè)置 templateUrl 屬性,在你要?jiǎng)?chuàng)建更多的通用指令時(shí)會(huì)顯得更加有用,比如說(shuō)用來(lái)顯示用戶信息的指令。例子:
<!-- 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)建了一個(gè)指令,當(dāng)AngularJS 找到一個(gè) <userinfo> 元素的時(shí)候就會(huì)激活它。AngularJS 加載指向 /myapp/html-templates/userinfo-template.html 的模板并解析它,它就像從一開(kāi)始就被嵌在上一級(jí) HTML 文件中一樣。
從指令中隔離 $scope
在上面的例子中,把 userinfo 指令硬綁定到了 $scope ,因?yàn)?HTML 模板是直接引用 textToInsert 屬性的。直接引用 $scope 讓指令在同一個(gè) controller 中的時(shí)候,非常難復(fù)用,因?yàn)?$scope在同一個(gè) controller 中的值都是一樣的。比如說(shuō),你在頁(yè)面的 HTML 中這樣寫的時(shí)候:
<!-- lang: js --> <userinfo></userinfo> <userinfo></userinfo>
這兩個(gè) <userinfo> 元素會(huì)被同樣的 HTML 模板取代,然后綁定到同樣的 $scope 變量上。結(jié)果就是兩個(gè) <userinfo> 元素將會(huì)被一模一樣的 HTML 代碼給替換了。
為了把兩個(gè) <userinfo> 元素綁定到 $scope 中的不同的值,你需要給 HTML 模板一個(gè) isolate scope。
所謂 isolate scope 是綁定在指令上的獨(dú)立的 scope 對(duì)象。下面是定義的例子:
<!-- lang: js -->
myapp.directive('userinfo', function() {
var directive = {};
directive.restrict = 'E';
directive.template = "User : {{user.firstName}} {{user.lastName}}";
directive.scope = {
user : "=user"
}
return directive;
})
請(qǐng)看 HTMl 模板中的兩個(gè)內(nèi)插指令 {{user.firstName}} 和 {{user.lastName}}。注意 user. 部分。以及directive.scope 屬性。 directive.scope 是個(gè)帶 user 屬性的 Javascript 對(duì)象。于是directive.scope 就成為了 isolate scope 對(duì)象,現(xiàn)在 HTML 模板被綁到了 directive.scope.user 上(通過(guò) {{user.firstName}} 和 {{user.lastName}} 內(nèi)插指令)。
directive.scope.user 被設(shè)置為 “=user” 。意思是說(shuō) directive.scope.user 和 scope 中的屬性綁在一起的(不是 isolate scope),scope 中的屬性通過(guò) user 屬性傳入 <userinfo> 元素。聽(tīng)起來(lái)挺費(fèi)勁的,直接看例子:
<!-- lang: js --> <userinfo user="jakob"></userinfo> <userinfo user="john"></userinfo>
這兩個(gè) <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ù)
如果你需要在你的指令中做更高級(jí)的操作,單純使用 HTML 模板做不到的時(shí)候,你可以考慮使用compile() 和 link() 函數(shù)。
compile() 和 link() 函數(shù)定義了指令如何修改匹配到的 HTML。
當(dāng)指令第一次被 AngularJS 編譯的時(shí)候 (在 HTML 中第一次被發(fā)現(xiàn)),compile() 函數(shù)被調(diào)用。compile() 函數(shù)將為該元素做一次性配置。
然后 compile() 函數(shù)以返回 link() 作為終結(jié)。link() 函數(shù)將在每次元素被綁到 $scope 數(shù)據(jù)時(shí),都被調(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ù)帶兩個(gè)參數(shù): element 和 attributes。
element 參數(shù)是 jqLite 包裝過(guò)的 DOM 元素。AngularJS 有一個(gè)簡(jiǎn)化版 jQuery 可以用于操作 DOM,所以對(duì) element 的 DOM 操作方式和你在 jQuery 中所知的一樣。
attributes 參數(shù)是包含了 DOM 元素中的所有的屬性集合的 Javascript 對(duì)象。因此,你要訪問(wèn)某個(gè)屬性你可以這樣寫 attributes.type。
link() 函數(shù)帶三個(gè)參數(shù): $scope,element 和 attributes。element 和attributes 參數(shù)和傳到 compile() 函數(shù)中的一樣。$scope 參數(shù)是正常的 scope 對(duì)象,或者當(dāng)你在指令定義對(duì)象中定義了 isolate scope 的時(shí)候,它就是 isolate scope。
compile() 和 link() 的命名相當(dāng)混亂。它們肯定是受編譯隊(duì)伍的啟發(fā)而命名的。我可以看到相似點(diǎn),不過(guò)一個(gè)編譯器是傳入一次,然后輸出。而指令則是配置一次 HTML 元素,然后在之后的 $scope 對(duì)象改變時(shí)進(jìn)行更新。
compile() 函數(shù)如果叫做 create(), init() 或者 configure()也許會(huì)更好。這樣可以表達(dá)出這個(gè)函數(shù)只會(huì)被調(diào)用一次的意思。
而 link() 函數(shù)如果叫 bind() 或者 render() 也許會(huì)更好,能更好的表達(dá)出這樣的意思,在指令綁定數(shù)據(jù)或者重綁定數(shù)據(jù)的時(shí)候,這個(gè)函數(shù)將會(huì)被調(diào)用。
下面是一個(gè)完整的例子,演示了指令使用 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í)行一次,因?yàn)?compile() 函數(shù)只執(zhí)行一次。
link() 替換 HTML 元素內(nèi)容,并且把背景顏色設(shè)置為黃色。
把設(shè)置 border 放在 compile(), 把背景顏色放在 link() 沒(méi)啥特別的理由。你可以把所有的操作都丟到 compile(),或者都放到 link()。如果都放到 compile() 它們只會(huì)被設(shè)置一次(你需要它們是常量的話)。如果放到了 link(),它們會(huì)在每次 HTML 元素被綁到 $scope 的時(shí)候都發(fā)生變化。當(dāng)你希望根據(jù) $scope 中的數(shù)據(jù)來(lái)設(shè)置 boarder 和背景顏色的時(shí)候這非常有用。
只設(shè)置 link() 函數(shù)
有時(shí)候你的指令可能不需要 compile() 。你只需要用到 link()。這種情況下,你可以直接設(shè)置指令定義對(duì)象中的 link() 函數(shù)。下面是一個(gè)對(duì)上面例子的修改,只用 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() 是完全一樣的。
通過(guò) Transclusion 封裝元素的指令
到目前為止,我們看到的所有例子,都是把匹配到的元素內(nèi)容給替換為指令的指定內(nèi)容,不管是通過(guò) Javascript 也好或者 HTML 模板也好。不過(guò)如果遇到內(nèi)容中有部分是開(kāi)發(fā)者可以指定的內(nèi)容的時(shí)候呢?比如說(shuō):
<!-- lang: js -->
<mytransclude>This is a transcluded directive {{firstName}}</mytransclude>
標(biāo)記為 <mytransclude> 的元素,它的部分內(nèi)容可以由開(kāi)發(fā)者設(shè)置。因此,這部分 HTML 不應(yīng)該被指令的 HTML 模板給替換。我們實(shí)際上是希望這部分 HTML 由 AngularJS 來(lái)處理的。這個(gè)處理叫做 “transclusion”。 1
為了能讓 AngularJS 把這部分 HTML 放到指令內(nèi)部處理,你必須設(shè)置指令定義對(duì)象的 transclude 屬性為 true。你還需要告訴 AngularJS,指令的 HTML 模板中哪一部分需要把 transcluded HTML 包含進(jìn)來(lái)。你可以通過(guò)插入 ng-transclude 屬性 (實(shí)際上,是一個(gè)指令) 到 HTML 模板的 HTML 元素中,標(biāo)記你想要添加 transcluded HTML 的元素。
下面是一個(gè) 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 來(lái)為我們處理這部分 HTML,讓內(nèi)插指令執(zhí)行。為了實(shí)現(xiàn)這個(gè)目的,我在指令定義對(duì)象中把 transclude 設(shè)置為 true。我還在 HTML 模板中用到了 ng-transclude 屬性。這個(gè)屬性告訴 AngularJS 什么元素需要插入到 transcluded HTML。
1: 說(shuō)實(shí)話,我沒(méi)看懂那個(gè)定義,說(shuō)的太TM難懂了,而且我好不爽寫代碼沒(méi)輸出的教程。只好自己動(dòng)手做做例子。我覺(jué)得其實(shí)應(yīng)該是這樣的,把目標(biāo)元素內(nèi)容作為一個(gè)整體,拿到 HTML 模板中來(lái),添加到 ng-transclude 指定的標(biāo)簽下。這個(gè)處理,我覺(jué)得應(yīng)該就是叫做 transcluded。比如說(shuō)剛才的例子(有些 bug,自己修正一下),因?yàn)?directive.transclude = true; ,所以原來(lái) <mytransclude> 元素內(nèi)的 HTML:
<!-- lang: js -->
This is a transcluded directive {{firstName}}
在激活指令 'mytransclude' 的時(shí)候,會(huì)被拿到 'mytransclude' 指令的模板中來(lái),放到被 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é)》
希望本文所述對(duì)大家AngularJS程序設(shè)計(jì)有所幫助。
相關(guān)文章
angular+ionic 的app上拉加載更新數(shù)據(jù)實(shí)現(xiàn)方法
這篇文章主要介紹了angular+ionic 的app上拉加載更新數(shù)據(jù)實(shí)現(xiàn)方法,需要的的朋友參考下2017-01-01
Angular-Ui-Router+ocLazyLoad動(dòng)態(tài)加載腳本示例
本篇文章主要介紹了Angular-Ui-Router+ocLazyLoad動(dòng)態(tài)加載腳本示例,可以提高加載速度,使用戶體驗(yàn)更好,有興趣的可以了解一下。2017-03-03
詳解使用angular-cli發(fā)布i18n多國(guó)語(yǔ)言Angular應(yīng)用
這篇文章主要介紹了詳解使用angular-cli發(fā)布i18n多國(guó)語(yǔ)言Angular應(yīng)用,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05
Angular應(yīng)用里異步打開(kāi)對(duì)話框技術(shù)詳解
這篇文章主要為大家介紹了Angular應(yīng)用里異步打開(kāi)對(duì)話框技術(shù)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
AngularJS實(shí)現(xiàn)表單手動(dòng)驗(yàn)證和表單自動(dòng)驗(yàn)證
本文是對(duì)AngularJS表單驗(yàn)證,手動(dòng)驗(yàn)證或自動(dòng)驗(yàn)證的講解,對(duì)學(xué)習(xí)JavaScript編程技術(shù)有所幫助,感興趣的小伙伴們可以參考一下2015-12-12
解決angular2在雙向數(shù)據(jù)綁定時(shí)[(ngModel)]無(wú)法使用的問(wèn)題
今天小編就為大家分享一篇解決angular2在雙向數(shù)據(jù)綁定時(shí)[(ngModel)]無(wú)法使用的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-09-09
Angular實(shí)現(xiàn)表單驗(yàn)證功能
這篇文章主要為大家詳細(xì)介紹了Angular實(shí)現(xiàn)表單驗(yàn)證功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11

