對於指令,可以把它簡單的理解成在特定DOM元素上啟動並執行函數,指令可以擴充這個元素的功能。
首先來看個完整的參數樣本再來詳細的介紹各個參數的作用及用法:
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[string]
restrict是一個可選的參數。用於指定該指令在DOM中以何種形式被聲明。預設值是A,即以屬性的形式來進行聲明。
可選值如下:
E(元素)
<my-directive></my-directive>
A(屬性,預設值)
<div my-directive="expression"></div>
C(類名)
<div class="my-directive:expression;"></div>
M(注釋)
<--directive:my-directive expression-->
一般考慮到瀏覽器的相容性,強烈建議使用預設的屬性就可以即即以屬性的形式來進行聲明。最後一種方式建議再不要求逼格指數的時候千萬不要用。
Code:
angular.module('app',[]) .directive('myDirective', function () { return { restrict: 'E', template: '<a href="http://www.baidu.com">百度</a>' }; })HtmlCode: <my-directive></my-directive>
效果:
priority[int]
大多數指令會忽略這個參數,使用預設值0,但也有些情境設定高優先順序是非常重要甚至是必須的。例如,ngRepeat將這個參數設定為1000,這樣就可以保證在同一元素上,它總是在其他指令之前被調用。
terminal[bool]
這個參數用來停止運行當前元素上比本指令優先順序低的指令。但同當前指令優先順序相同的指令還是會被執行。
例如:ngIf的優先順序略高於ngView(它們操控的實際就是terminal參數),如果ngIf的運算式值為true,ngView就可以被正常執行,但如果ngIf運算式的值為false,由於ngView的優先順序較低就不會被執行。
template[string or function]
template參數是可選的,必須被設定為以下兩種形式之一:
一段HTML文本;
一個可以接受兩個參數的函數,參數為tElement和tAttrs,並返回一個代表模板的字串。tElement和tAttrs中的t代表template,是相對於instance的。
首先示範下第二種用法:
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[string or function]
templateUrl是可選的參數,可以是以下類型:
一個代表外部HTML檔案路徑的字串;
一個可以接受兩個參數的函數,參數為tElement和tAttrs,並返回一個外部HTML檔案路徑的字串。
無論哪種方式,模板的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[bool]
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參數[bool or object]
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>
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
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
my-dialog-close.html<div class="alert"> <a href class="close" ng-click="close()">×</a> <div ng-transclude></div></div>
indext.html
<div ng-controller="Controller"> <my-dialog ng-hide="dialogIsHidden" on-close="hideDialog()"> Check out the contents, {{name}}! </my-dialog></div>
如果指令使用了transclude參數,那麼在控制器無法正常監聽資料模型的變化了。建議在連結函數裡使用$watch服務。
controller[string or function]
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">Google</div>
僅在compile參數中使用transcludeFn是推薦的做法。link函數可以將指令互相隔離開來,而controller則定義可複用的行為。如果我們希望將當前指令的API暴露給其他指令使用,可以使用controller參數,否則可以使用link來構造當前指令元素的功能性(即內部功能)。如果我們使用了scope.$watch()或者想要與DOM元素做即時的互動,使用連結會是更好的選擇。使用了嵌入,控制器中的範圍所反映的範圍可能與我們所期望的不一樣,這種情況下,$scope對象無法保證可以被正常更新。當想要同當前螢幕上的範圍互動時,可以使用傳入到link函數中的scope參數。
controllerAs[string]
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[string or string[]]
require為字串代表另外一個指令的名字。require會將控制器注入到其所指定的指令中,並作為當前指令的連結函數的第四個參數。字串或數組元素的值是會在當前指令的範圍中使用的指令名稱。在任何情況下,ng編譯器在尋找子控制器時都會參考當前指令的模板。
- 如果不使用^首碼,指令只會在自身的元素上尋找控制器。指令定義只會尋找定義在指令作當前用域中的ng-model=""
- 如果使用?首碼,在當前指令中沒有找到所需要的控制器,會將null作為傳給link函數的第四個參數。
- 如果添加了^首碼,指令會在上遊的指令鏈中尋找require參數所指定的控制器。
- 如果添加了?^ 將前面兩個選項的行為組合起來,我們可選擇地載入需要的指令並在父指令鏈中進行尋找
- 如果沒有任何首碼,指令將會在自身所提供的控制器中進行尋找,如果沒有找到任何控制器(或具有指定名字的指令)就拋出一個錯誤
compile【object or function】
compile選項本身並不會被頻繁使用,但是link函數則會被經常使用。本質上,當我們設定了link選項,實際上是建立了一個postLink() 連結函數,以便compile() 函數可以定義連結函數。通常情況下,如果設定了compile函數,說明我們希望在指令和即時資料被放到DOM中之前進行DOM操作,在這個函數中進行諸如添加和刪除節點等DOM操作是安全的。
compile和link選項是互斥的。如果同時設定了這兩個選項,那麼會把compile所返回的函數當作連結函數,而link選項本身則會被忽略。
編譯函數負責對模板DOM進行轉換。連結函數負責將範圍和DOM進行連結。 在範圍同DOM連結之前可以手動操作DOM。在實踐中,編寫自訂指令時這種操作是非常罕見的,但有幾個內建指令提供了這樣的功能。
link
compile: function(tEle, tAttrs, transcludeFn) { //todo: return function(scope, ele, attrs) { // 連結函數 };
連結函數是可選的。如果定義了編譯函數,它會返回連結函數,因此當兩個函數都定義時,編譯函數會重載連結函數。如果我們的指令很簡單,並且不需要額外的設定,可以從工廠函數(回呼函數)返回一個函數來代替對象。如果這樣做了,這個函數就是連結函數。
ngModel
它提供更底層的API來處理控制器內的資料,這個API用來處理資料繫結、驗證、 CSS更新等不實際操作DOM的事情,ngModel 控制器會隨 ngModel 被一直注入到指令中,其中包含了一些方法。為了訪問ngModelController必須使用require設定.
ngModelController常用的元素如下:
1.為了設定範圍中的視圖值,需要調用 ngModel.$setViewValue() 函數。
$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); }); } }); }); } }; });
2.$render方法可以定義視圖具體的渲染方式
3.屬性
1. $viewValue
$viewValue屬性儲存著更新視圖所需的實際字串。
2. $modelValue
$modelValue由資料模型持有。 $modelValue和$viewValue可能是不同的,取決於$parser流水線是否對其進行了操作。
3. $parsers
$parsers 的值是一個由函數組成的數組,其中的函數會以流水線的形式被逐一調用。ngModel 從DOM中讀取的值會被傳入$parsers中的函數,並依次被其中的解析器處理。
4. $formatters
$formatters的值是一個由函數組成的數組,其中的函數會以流水線的形式在資料模型的值發生變化時被逐一調用。它和$parser流水線互不影響,用來對值進行格式化和轉換,以便在綁定了這個值的控制項中顯示。
5. $viewChangeListeners
$viewChangeListeners的值是一個由函數組成的數組,其中的函數會以流水線的形式在視圖中的值發生變化時被逐一調用。通過$viewChangeListeners,可以在無需使用$watch的情況下實作類別似的行為。由於傳回值會被忽略,因此這些函數不需要傳回值。
6. $error
$error對象中儲存著沒有通過驗證的驗證器名稱以及對應的錯誤資訊。
7. $pristine
$pristine的值是布爾型的,可以告訴我們使用者是否對控制項進行了修改。
8. $dirty
$dirty的值和$pristine相反,可以告訴我們使用者是否和控制項進行過互動。
9. $valid
$valid值可以告訴我們當前的控制項中是否有錯誤。當有錯誤時值為false, 沒有錯誤時值為true。
10. $invalid
$invalid值可以告訴我們當前控制項中是否存在至少一個錯誤,它的值和$valid相反。
以上就是關於AngularJS的指令知識資料整理,後續繼續補充,謝謝大家對本站的支援!