AngularJS Directive 自定義指令(我最喜歡AngularJs的功能之一)javascript
一:何時咱們會用到directivecss
1.使html更具語義化,不用深刻了解研究代碼的邏輯即可知道大體邏輯。html
2.抽象出一個自定義組件,能夠重複使用。java
二:directive的定義及其使用方法angularjs
1.下面是一個directive參數詳細模板express
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:app
(布爾型),可選參數,值爲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則被忽略。