標籤:
AngularJS Directive 自訂指令(我最喜歡AngularJs的功能之一)
一:什麼時候我們會用到directive
1.使html更具語義化,不用深入瞭解研究代碼的邏輯便可知道大致邏輯。
2.抽象出一個自訂群組件,可以重複使用。
二:directive的定義及其使用方法
1.下面是一個directive參數詳細模板
angular.module(‘app‘,[]);//申明一個調用angularjs塊 angular.module(‘app‘).directive(‘directiveName‘, function factory() { var directiveDefinitionObject = { priority: 0,
restrict:‘A‘, template: ‘<div></div>‘, templateUrl: ‘directive.html‘, replace: false, transclude: false, scope: false, compile: function compile(tElement, tAttrs, transclude) { return { pre: function preLink(scope, iElement, iAttrs, controller) { ... }, post: function postLink(scope, iElement, iAttrs, controller) { ... } } }, link: function postLink(scope, iElement, iAttrs) { ... } }; return directiveDefinitionObject; });
2.參數詳解
priority:
(數字),選擇性參數,指明指令的優先順序,若在單個DOM上有多個指令,則優先順序高的先執行;
terminal:
(布爾型),選擇性參數,值為true或false,若設定為true,則優先順序低於此指令的其他指令則無效,不會被調用(優先順序相同的還是會執行);
restrict:
(字串)選擇性參數,指明指令在DOM裡面以什麼形式被聲明;
取值有:E(元素),A(屬性),C(類),M(注釋),其中預設值為A;當然也可以兩個一起用,比如EA.表示即可以是元素也可以是屬性。
E(元素):<directiveName></directiveName>
A(屬性):<div directiveName=‘expression‘></div>
C(類): <div class=‘directiveName‘></div>
M(注釋):<--directive:directiveName expression-->
一般情況下E/A/C用得比較多。
template:
(字串或函數),選擇性參數
例:(1)字串時:
<!DOCTYPE html> <html lang="zh" ng-app="app"> <head> <meta charset="UTF-8"> <title>AngularJS Directive</title> <script type="text/javascript" src="angular.min.js"></script> </head> <body> <hello-world></hello-world> </body> <script type="text/javascript"> angular.module(‘app‘, []); angular.module.(‘app‘).directive(‘helloWorld‘, function() { return { restrict: ‘E‘, template: ‘<div><h1>Hi I am hello world.</h1></div>‘, replace: true }; }); </script> </html>
(2)函數時:有兩個參數tElement和tAttrs
tElement:使用此指令的元素
tAttrs:該指令元素上的屬性
<!DOCTYPE html> <html lang="zh" ng-app="app"> <head> <meta charset="UTF-8"> <title>AngularJS Directive</title> <script type="text/javascript" src="angular.min.js"></script> </head> <body> <hello-world></hello-world> <hello-world2 title = ‘I am a Hello World 2.‘></hello-world2> </body> <script type="text/javascript"> angular.module(‘app‘, []); app.directive(‘helloWorld‘, function() { return { restrict: ‘E‘, template: ‘<div><h1>Hi I am a Hello Worl.</h1></div>‘, replace: true }; }); app.directive("helloWorld2",function(){ return{ restrict:‘EAC‘, template: function(tElement,tAttrs){ var _html = ‘‘; _html += ‘<div>‘+tAttrs.title+‘</div>‘; return _html; } }; }); </script> </html>
templateUrl:
(字串或者函數)選擇性參數
字串:代表HTML檔案路徑的字串
函數:可接收兩個參數tElement和tAttrs(大致同上)
(PS:由於載入html模板是通過非同步載入的,若載入大量的模板會拖慢網站的速度,所以我們可以先緩衝模板)
例:(把要載入的頁面 先載入好 包含在你需要的html裡)
<!DOCTYPE html> <html lang="zh" ng-app="app"> <head> <meta charset="UTF-8"> <title>AngularJS Directive</title> <script type="text/javascript" src="angular.min.js"></script> </head> <body> <hello-world></hello-world> </body> <script type="text/javascript"> angular.module(‘app‘, []); angular.module("app").directive(‘helloWorld‘, function() { return { restrict: ‘E‘, templateUrl: ‘hello.html‘, replace: true }; }); </script> <script type=‘text/ng-template‘ id=‘hello.html‘> <div><h1>Hi I am Hello world.</h1></div> </script> </html>
還有一種方法:
angular.module("app").run(["$templateCache", function($templateCache) { $templateCache.put("hello.html", "<div><h1>Hi I am Hello world.</h1></div>"); }]);
replace:
(布爾值):預設值是false. true:替換掉指令即(<hello-world></hello-world>),false:不替換
transclude:(是否想要指令內部的內容被模板替換掉)
(布爾值):預設值是false. true:需要和ng transclude一起使用
例:template:"<div>hello every <div ng-transclude></div></div>"
這時,指令內部的內容會嵌入到ng-transclude這個div中。也就是變成了<div>hello every <div>這是指令內部的內容</div></div>。
scope:
1)預設值false。表示繼承父範圍;(繼承不隔離)
2)true。表示繼承父範圍,並建立自己的範圍(子範圍);(繼承隔離)
3){}。表示建立一個全新的隔離範圍;(不繼承隔離)
(PS:當你想要建立一個可重用的組件時隔離範圍是一個很好的選擇,通過隔離範圍我們確保指令是‘獨立‘的,並可以輕鬆地插入到任何HTML app中,並且這種做法防止了父範圍被汙染;)
下面針對隔離範圍進行詳細講解:
a.隔離範圍怎樣去訪問父範圍:
Directive 在使用隔離 scope 的時候,提供了三種方法同隔離之外的地方互動.
1). @
(用來訪問 directive 外部環境定義的字串值,主要是通過 directive 所在的標籤屬性綁定外部字串值。這種綁定是單向的,即父 scope 的綁定變化,directive 中的 scope 的屬性會同步變化, 而隔離 scope 中的綁定變化,父 scope 是不知道的。)PS:相當於繼承隔離
scope:{
name:"@"
}
2). &
(& 方式提供一種途經是 directive 能在父 scope 的上下文中執行一個運算式。當 directive 中有什麼動作需要更新到父 scope 中的時候,可以在父 scope 上下文中執行一段代碼或者一個函數。PS:即綁定的是方法)
<!DOCTYPE html> <html lang="zh" ng-app="app"> <head> <meta charset="UTF-8"> <title>AngularJS Directive</title> <script type="text/javascript" src="angular.min.js"></script> </head> <body> <div ng-controller="myController"> <div>父scope: <div>Say:{{value}}</div> </div> <div>隔離scope: <div isolated-directive action="click()"></div> </div> </div> </body> <script type="text/javascript"> angular.module(‘app‘, []); app.controller("myController", function ($scope) { $scope.value = "hello world"; $scope.click = function () { $scope.value = Math.random(); }; }).directive("isolatedDirective", function () { return { scope: { action: "&" }, template: ‘<input type="button" value="在directive中執行父scope定義的方法" ng-click="action()"/>‘ } }) </script> </html>
3). =
(通過 directive 的 attr 屬性的值在局部 scope 的屬性和父 scope 屬性名稱之間建立雙向繫結。
意思是,當你想要一個雙向繫結的屬性的時候,你可以使用=來引入外部屬性。無論是改變父 scope 還是隔離 scope 裡的屬性,父 scope 和隔離 scope 都會同時更新屬性值,因為它們是雙向繫結的關係。)
例:
<!DOCTYPE html> <html lang="zh" ng-app="app"> <head> <meta charset="UTF-8"> <title>AngularJS Directive</title> <script type="text/javascript" src="angular.min.js"></script> </head> <body> <div ng-controller="myController"> <div>父scope: <div>Say:{{user.name}}<br>改變父scope的name:<input type="text" value="" ng-model="userBase.name"/></div> </div> <div>隔離scope: <div isolated-directive user="userBase"></div> </div> </div> </body> <script type="text/javascript"> angular.module(‘app‘, []); angular.module.("app").controller("myController", function ($scope) { $scope.userBase = { name: ‘hello‘, id: 1 }; }).directive("isolatedDirective", function () { return { scope: { user: "=" }, template: ‘Say:{{user.name}} <br>改變隔離scope的name:<input type="buttom" value="" ng-model="user.name"/>‘ } }) </script> </html>
controller:
(可以是一個字串或者函數)
angular.module(‘app‘, []) angular.module.("app").directive(‘myDirective‘, function() { restrict: ‘A‘, controller: ‘DefinitionController‘ }) // 應用中其他的地方,可以是同一個檔案或被index.html包含的另一個檔案 angular.module(‘app‘) .controller(‘DefinitionController‘, function($scope, $element, $a也可以直接在指令內部的定義為匿名函數,同樣我們可以再這裡注入任何服務($log,$timeout等等) js:angular.module(‘app‘,[]) .directive(‘myDirective‘, function() { restrict: ‘A‘, controller: function($scope, $element, $attrs, $transclude) { // 控制器邏輯放在這裡 } });
另外還有一些特殊的服務(參數)可以注入
(1)$scope,與指令元素相關聯的範圍
(2)$element,當前指令對應的 元素
(3)$attrs,由當前元素的屬性群組成的對象
(4)$transclude,嵌入連結函數,實際被執行用來複製元素和操作DOM的函數
注意: 除非是用來定義一些可複用的行為,一般不推薦在這使用。
指令的控制器和link函數(後面會講)可以進行互換。區別在於,控制器主要是用來提供可在指令間複用的行為但link連結函數只能在當前內部指令中定義行為,且無法再指令間複用。
<!DOCTYPE html> <html lang="zh" ng-app="app"> <head> <meta charset="UTF-8"> <title>AngularJS入門學習</title> <script type="text/javascript" src="angular.min.js"></script> </head> <hello mycolor ="red">I am Hello World.</hello> </body> <script type="text/javascript"> angular.module(‘app‘, []); angular.module("app").directive(‘hello‘, function() { return { restrict: ‘EA‘, transclude: true, //注意此處必須設定為true controller: function ($scope, $element,$attrs,$transclude,$log) { //在這裡你可以注入你想注入的服務 $transclude(function (clone) { var a = angular.element(‘<p>‘); a.css(‘color‘, $attrs.mycolor); a.text(clone.text()); $element.append(a); }); $log.info("hello everyone"); } }; }); </script> </html>
這裡如果我們想要實用的父範圍:$scope.$parent
新的範圍:$scope.$parent.new()
controllerAs
設定控制器的別名(angular1.2給我們帶來的新的文法糖)
例:
<script> angular.module(‘app‘,[]).directive(‘myDirective‘, function () { return { restrict: ‘EA‘, transclude: true, controller:‘someController‘, controllerAs:‘mainController‘ //..其他配置 }; }); </script>
require
字串或數組
字串代表另一個指令的名字,作為link函數的第四個參數。假設現在我們要編寫兩個指令,兩個指令中的link連結函數中存在有很多重合的方法,這時候我們就可以將這些重複的方法寫在第三個指令的controller中(上面也講到controller經常用來提供指令間的複用行為)然後在這兩個指令中,require這個擁有controller欄位的的指令(第三個指令),最後通過link連結函數的第四個參數就可以引用這些重合的方法了。
例
<!doctype html> <html ng-app="app"> <head> <script src="angular.min.js"></script> </head> <body> <outer-directive> <inner-directive></inner-directive> <inner-directive2></inner-directive2> </outer-directive> <script> angular.module(‘app‘, []); angular.module("app").directive(‘outerDirective‘, function() { return { scope: {}, restrict: ‘AE‘, controller: function($scope) { this.say = function(someDirective) { console.log(‘Show:‘ + someDirective.message); }; } }; }); angular.module("app").directive(‘innerDirective‘, function() { return { scope: {}, restrict: ‘AE‘, require: ‘^outerDirective‘, link: function(scope, elem, attrs, controllerInstance) { scope.message = "Hi,I am Ruby."; controllerInstance.say(scope); } }; }); app.directive(‘innerDirective2‘, function() { return { scope: {}, restrict: ‘AE‘, require: ‘^outerDirective‘, link: function(scope, elem, attrs, controllerInstance) { scope.message = "Hi,I am Hello World."; controllerInstance.say(scope); } }; }); </script> </body> </html>
require參數有四種:
1)沒有首碼,指令會在自身提供的控制器中進行尋找,如果找不到任何控制器,則會拋出一個error
2)?如果在當前的指令沒有找到所需的控制器,則會將null傳給link串連函數的第四個參數
3)^如果在當前的指令沒有找到所需的控制器,則會尋找父元素的控制器
4)?^組合
compile function
function compile(tElement, tAttrs, transclude) { ... }
編譯函數用來修改模板dom的(不常用)
需要用到編譯函數例如:ngTrepeat;ngView(需要非同步載入內容的)。
各參數意義:
tElement - template element 該指令所在的元素。
tAttrs - template attributes 指令元素上所聲明的屬性。
transclude - 一個嵌入的連結函數function(scope, cloneLinkingFn)。
注意:在編譯函數裡面不要進行任何DOM變形之外的操作。 更重要的,DOM監聽事件的註冊應該在連結函數中做,而不是編譯函數中。 編譯函數可以返回一個對象或者函數。 返回函數 - 等效於在編譯函數不存在時,使用設定物件的link屬性註冊的連結函數。 返回對象 - 返回一個通過pre或post屬性註冊了函數的對象。參考下面pre-linking和post-liking函數的解釋。
link function
function link(scope, iElement, iAttrs, controller) { ... }
連結函數負責註冊DOM事件和更新DOM。它是在模板被複製之後執行的,它也是大部分指令邏輯代碼編寫的地方。
scope - 指令需要監聽的範圍。
iElement - instance element - 指令所在的元素。只有在postLink函數中對元素的子項目進行操作才是安全的,因為那時它們才已經全部連結好。
iAttrs - instance attributes - 執行個體屬性,一個標準化的、所有聲明在當前元素上的屬性列表,這些屬性在所有連結函數間是共用的。
controller - 控制器執行個體,也就是當前指令通過require請求的指令someDirective內部的controller。比如:someDirective指令中的controller:function(){this.Say = function(){}},那麼,在當前指令的link函數中,你就可以通過controller.Say進行調用了。
Pre-linking function 在子項目被連結前執行。不能用來進行DOM的變形,以防連結函數找不到正確的元素來連結。
Post-linking function 所有元素都被連結後執行。
compile function 和 link function需要注意的地方:兩者是互斥的,compile function負責對模板dom進行修改,link function負責將範圍和dom進行串連。
當兩者共存時,compile function返回的函數當作link function,link function則被忽略。
AngularJS 講解,四 Directive