轉自:http://www.cnblogs.com/rohelm/p/4051437.htmlhtml
對於指令,能夠把它簡單的理解成在特定DOM元素上運行的函數,指令能夠擴展這個元素的功能。express
首先來看個完整的參數示例再來詳細的介紹各個參數的做用及用法:數組
angular.module('myApp', []) .directive('myDirective', function() { return { restrict: String, priority: Number, terminal: Boolean, template: String or Template Function: function(tElement, tAttrs) {...}, templateUrl: String, replace: Boolean or String, scope: Boolean or Object, transclude: Boolean, controller: String or function(scope, element, attrs, transclude, otherInjectables) { ... }, controllerAs: String, require: String, link: function(scope, iElement, iAttrs) { ... }, compile: // 返回一個對象或鏈接函數,以下所示: function(tElement, tAttrs, transclude) { return { pre: function(scope, iElement, iAttrs, controller) { ... }, post: function(scope, iElement, iAttrs, controller) { ... } } return function postLink(...) { ... } } }; });
restrict是一個可選的參數。用於指定該指令在DOM中以何種形式被聲明。默認值是A,即以屬性的形式來進行聲明。
可選值以下:
E(元素)瀏覽器
<my-directive></my-directive>
A(屬性,默認值)緩存
<div my-directive="expression"></div>
C(類名)安全
<div class="my-directive:expression;"></div>
M(註釋)app
<--directive:my-directive expression-->
通常考慮到瀏覽器的兼容性,強烈建議使用默認的屬性就能夠即即以屬性的形式來進行聲明。最後一種方式建議再不要求逼格指數的時候千萬不要用。ide
Code:函數
angular.module('app',[]) .directive('myDirective', function () { return { restrict: 'E', template: '<a href="http://www.baidu.com">百度</a>' }; })
HtmlCode:
<my-directive></my-directive>
效果:post
大多數指令會忽略這個參數,使用默認值0,但也有些場景設置高優先級是很是重要甚至是必須的。例如,ngRepeat將這個參數設置爲1000,這樣就能夠保證在同一元素上,它老是在其餘指令以前被調用。
這個參數用來中止運行當前元素上比本指令優先級低的指令。但同當前指令優先級相同的指令仍是會被執行。
例如:ngIf的優先級略高於ngView(它們操控的實際就是terminal參數),若是ngIf的表達式值爲true,ngView就能夠被正常執行,但若是ngIf表達式的值爲false,因爲ngView的優先級較低就不會被執行。
template參數是可選的,必須被設置爲如下兩種形式之一:
首先演示下第二種用法:
angular.module('app',[]) .directive('myDirective', function () { return { restrict: 'EAC', template: function (elem, attr) { return "<a href='" + attr.value + "'>" + attr.text + "</a>"; } }; })
HtmlCode:(效果同上,不作演示了)
<my-directive value="http://www.baidu.com" text="百度"></my-directive> <div my-directive value="http://www.baidu.com" text="百度"></div>
templateUrl是可選的參數,能夠是如下類型:
不管哪一種方式,模板的URL都將經過ng內置的安全層,特別是$getTrustedResourceUrl,這樣能夠保護模板不會被不信任的源加載。 默認狀況下,調用指令時會在後臺經過Ajax來請求HTML模板文件。加載大量的模板將嚴重拖慢一個客戶端應用的速度。爲了不延遲,能夠在部署應用以前對HTML模板進行緩存。
Code:
angular.module('app',[]) .directive('myDirective', function () { return { restrict: 'AEC', templateUrl: function (elem, attr) { return attr.value + ".html"; //固然這裏咱們能夠直接指定路徑,同時在模板中能夠包含表達式 } }; })
replace是一個可選參數,若是設置了這個參數,值必須爲true,由於默認值爲false。默認值意味着模板會被看成子元素插入到調用此指令的元素內部,
例如上面的示例默認值狀況下,生成的html代碼以下:
<my-directive value="http://www.baidu.com" text="百度"><a href="http://www.baidu.com">百度</a></my-directive>
若是設置replace=true
<a href="http://www.baidu.com" value="http://www.baidu.com" text="百度">百度</a>
據我觀察,這種效果只有設置restrict="E"的狀況下,纔會表現出實際效果。
介紹完基本的指令參數後,就要涉及到更重要的做用域參數了...
scope參數是可選的,能夠被設置爲true或一個對象。默認值是false。
若是一個元素上有多個指令使用了隔離做用域,其中只有一個能夠生效。只有指令模板中的根元素能夠得到一個新的做用域。所以,對於這些對象來講scope默認被設置爲true。內置指令ng-controller的做用,就是從父級做用域繼承並建立一個新的子做用域。它會建立一個新的從父做用域繼承而來的子做用域。這裏的繼承就不在贅述,和麪向對象中的繼承基本是一直的。
首先咱們來分析一段代碼:
<div ng-app="app" ng-init="name= '祖父'"> <div ng-init="name='父親'"> 第一代:{{ name }} <div ng-init="name= '兒子'" ng-controller="SomeController"> 第二代: {{ name }} <div ng-init="name='孫子'"> 第三代: {{ name }} </div> </div> </div> </div>
咱們發現第一代,咱們初始化name爲父親,可是第二代和第三代實際上是一個做用域,那麼他們的name實際上是一個對象,所以出現的效果以下:
第一代:父親 第二代: 孫子 第三代: 孫子
咱們在修改一下代碼,把第三代隔離開來再看看效果:
<div ng-app="app"ng-init="name= '祖父'"> <div ng-init="name='父親'"> 第一代:{{ name }} <div ng-init="name= '兒子'" ng-controller="SomeController"> 第二代: {{ name }} <div ng-init="name='孫子'" ng-controller="SecondController"> 第三代: {{ name }} </div> </div> </div> </div>
JsCode:
angular.module('app', []) .controller('SomeController',function($scope) { }) .controller('SecondController', function ($scope) { })
效果以下:
第一代:父親 第二代: 兒子 第三代: 孫子
在修改下代碼來看看繼承:
<div ng-app="app"ng-init="name= '祖父的吻'"> <div> 第一代:{{ name }} <div ng-controller="SomeController"> 第二代: {{ name }} <div ng-controller="SecondController"> 第三代: {{ name }} </div> </div> </div> </div>
效果以下:
第一代:祖父的吻 第二代: 祖父的吻 第三代: 祖父的吻
若是要建立一個可以從外部原型繼承做用域的指令,將scope屬性設置爲true,簡單來講就是可繼承的隔離,即不能反向影響父做用域。
再來看個例子:
angular.module('myApp', []) .controller('MainController', function ($scope) { }) .directive('myDirective', function () { return { restrict: 'A', scope:false,//切換爲{},true測試 priority: 100, template: '<div>內部:{{ myProperty }}<input ng-model="myProperty"/></div>' }; });
Html代碼:
<div ng-controller='MainController' ng-init="myProperty='Hello World!'"> 外部: {{ myProperty}} <input ng-model="myProperty" /> <div my-directive></div> </div>
當咱們改變scope的值咱們會發現
false:繼承但不隔離
true:繼承並隔離
{}:隔離且不繼承
transclude是一個可選的參數。默認值是false。嵌入一般用來建立可複用的組件,典型的例子是模態對話框或導航欄。咱們能夠將整個模板,包括其中的指令經過嵌入所有傳入一個指令中。指令的內部能夠訪問外部指令的做用域,而且模板也能夠訪問外部的做用域對象。爲了將做用域傳遞進去,scope參數的值必須經過{}或true設置成隔離做用域。若是沒有設置scope參數,那麼指令內部的做用域將被設置爲傳入模板的做用域。
只有當你但願建立一個能夠包含任意內容的指令時,才使用transclude: true。
咱們來看兩個例子-導航欄:
<div side-box title="TagCloud"> <div class="tagcloud"> <a href="">Graphics</a> <a href="">ng</a> <a href="">D3</a> <a href="">Front-end</a> <a href="">Startup</a> </div> </div>
JsCode:
angular.module('myApp', []) .directive('sideBox', function() { return { restrict: 'EA', scope: { title: '@' }, transclude: true, template: '<div class="sidebox"><div class="content"><h2 class="header">' + '{{ title }}</h2><span class="content" ng-transclude></span></div></div>' }; });
這段代碼告訴ng編譯器,將它從DOM元素中獲取的內容放到它發現ng-transclude指令的地方。
再來你看個官網的例子:
angular.module('docsIsoFnBindExample', []) .controller('Controller', ['$scope', '$timeout', function($scope, $timeout) { $scope.name = 'Tobias'; $scope.hideDialog = function () { $scope.dialogIsHidden = true; $timeout(function () { $scope.dialogIsHidden = false; }, 2000); }; }]) .directive('myDialog', function() { return { restrict: 'E', transclude: true, scope: { 'close': '&onClose' }, templateUrl: 'my-dialog-close.html' }; });
my-dialog-close.html
<div class="alert"> <a href class="close" ng-click="close()">×</a> <div ng-transclude></div> </div>
index.html
<div ng-controller="Controller"> <my-dialog ng-hide="dialogIsHidden" on-close="hideDialog()"> Check out the contents, {{name}}! </my-dialog> </div>
若是指令使用了transclude參數,那麼在控制器沒法正常監聽數據模型的變化了。建議在連接函數裏使用$watch服務。
controller參數能夠是一個字符串或一個函數。當設置爲字符串時,會以字符串的值爲名字,來查找註冊在應用中的控制器的構造函數.
angular.module('myApp', []) .directive('myDirective', function() { restrict: 'A', controller: 'SomeController' })
能夠在指令內部經過匿名構造函數的方式來定義一個內聯的控制器
angular.module('myApp',[]) .directive('myDirective', function() { restrict: 'A', controller: function($scope, $element, $attrs, $transclude) { // 控制器邏輯放在這裏 } });
咱們能夠將任意能夠被注入的ng服務注入到控制器中,即可以在指令中使用它了。控制器中也有一些特殊的服務能夠被注入到指令當中。這些服務有:
1. $scope
與指令元素相關聯的當前做用域。
2. $element
當前指令對應的元素。
3. $attrs
由當前元素的屬性組成的對象。
<div id="aDiv"class="box"></div> 具備以下的屬性對象: { id: "aDiv", class: "box" }
4. $transclude
嵌入連接函數會與對應的嵌入做用域進行預綁定。transclude連接函數是實際被執行用來克隆元素和操做DOM的函數。
angular.module('myApp',[]) .directive('myLink', function () { return { restrict: 'EA', transclude: true, controller: function ($scope, $element,$attrs,$transclude) { $transclude(function (clone) { var a = angular.element('<a>'); a.attr('href', $attrs.value); a.text(clone.text()); $element.append(a); }); } }; });
html
<my-link value="http://www.baidu.com">百度</my-link> <div my-link value="http://www.google.com">谷歌</div>
僅在compile參數中使用transcludeFn是推薦的作法。link函數能夠將指令互相隔離開來,而controller則定義可複用的行爲。若是咱們但願將當前指令的API暴露給其餘指令使用,可使用controller參數,不然可使用link來構造當前指令元素的功能性(即內部功能)。若是咱們使用了scope.$watch()或者想要與DOM元素作實時的交互,使用連接會是更好的選擇。使用了嵌入,控制器中的做用域所反映的做用域可能與咱們所指望的不同,這種狀況下,$scope對象沒法保證能夠被正常更新。當想要同當前屏幕上的做用域交互時,可使用傳入到link函數中的scope參數。
controllerAs參數用來設置控制器的別名,這樣就能夠在視圖中引用控制器甚至無需注入$scope。
<div ng-controller="MainController as main"> <input type="text" ng-model="main.name" /> <span>{{ main.name }}</span> </div>
JsCode:
angular.module('myApp',[]) .controller('MainController', function () { this.name = "Halower"; });
控制器的別名使路由和指令具備建立匿名控制器的強大能力。這種能力能夠將動態的對象建立成爲控制器,而且這個對象是隔離的、易於測試。
require爲字符串表明另一個指令的名字。require會將控制器注入到其所指定的指令中,並做爲當前指令的連接函數的第四個參數。字符串或數組元素的值是會在當前指令的做用域中使用的指令名稱。在任何狀況下,ng編譯器在查找子控制器時都會參考當前指令的模板。
compile選項自己並不會被頻繁使用,可是link函數則會被常用。本質上,當咱們設置了link選項,其實是建立了一個postLink() 連接函數,以便compile() 函數能夠定義連接函數。一般狀況下,若是設置了compile函數,說明咱們但願在指令和實時數據被放到DOM中以前進行DOM操做,在這個函數中進行諸如添加和刪除節點等DOM操做是安全的。
compile和link選項是互斥的。若是同時設置了這兩個選項,那麼會把compile所返回的函數看成連接函數,而link選項自己則會被忽略。
編譯函數負責對模板DOM進行轉換。連接函數負責將做用域和DOM進行連接。 在做用域同DOM連接以前能夠手動操做DOM。在實踐中,編寫自定義指令時這種操做是很是罕見的,但有幾個內置指令提供了這樣的功能。
compile: function(tEle, tAttrs, transcludeFn) { //todo: return function(scope, ele, attrs) { // 連接函數 };
連接函數是可選的。若是定義了編譯函數,它會返回連接函數,所以當兩個函數都定義時,編譯函數會重載連接函數。若是咱們的指令很簡單,而且不須要額外的設置,能夠從工廠函數(回調函數)返回一個函數來代替對象。若是這樣作了,這個函數就是連接函數。
它提供更底層的API來處理控制器內的數據,這個API用來處理數據綁定、驗證、 CSS更新等不實際操做DOM的事情,ngModel 控制器會隨 ngModel 被一直注入到指令中,其中包含了一些方法。爲了訪問ngModelController必須使用require設置.
ngModelController經常使用的元素以下:
$setViewValue() 方法適合於在自定義指令中監聽自定義事件(好比使用具備回調函數的jQuery插件),咱們會但願在回調時設置$viewValue並執行digest循環。
angular.module('myApp') .directive('myDirective', function() { return { require: '?ngModel', link: function(scope, ele, attrs, ngModel) { if (!ngModel) return; $(function() { ele.datepicker({
//回調函數 onSelect: function(date) { // 設置視圖和調用 apply scope.$apply(function() { ngModel.$setViewValue(date); }); } }); }); } }; });