angular.module("app",[]).directive("directiveName",function(){
return{
//通過設置項來定義
};
})
其中return返回的對象包含很多參數,下面一一說明

(字符串)可選參數,指明指令在DOM里面以什么形式被聲明;
取值有:E(元素),A(屬性),C(類),M(注釋),其中默認值為A;
E(元素):<directiveName></directiveName>
A(屬性):<div directiveName='expression'></div>
C(類): <div class='directiveName'></div>
M(注釋):<--directive:directiveName expression-->

例如restrict:‘EA’ 則表示指令在DOM里面可用元素形式和屬性形式被聲明;
一般來說,當你創建一個有自己模板的組件的時候,需要使用元素名,如果僅僅是為為已有元素添加功能的話,就使用屬性名
(數字),可選參數,指明指令的優先級,若在單個DOM上有多個指令,則優先級高的先執行;
設置指令的優先級算是不常用的
比較特殊的的例子是,angularjs內置指令的ng-repeat的優先級為1000,ng-init的優先級為450;
(布爾型),可選參數,可以被設置為true或false,若設置為true,則優先級低於此指令的其他指令則無效,不會被調用(優先級相同的還是會執行)
angular.module("app",[]).directive("hello",function(){
return{
restrict:'EA',
template:"<div><h3>hello world</h3></div>"
};
})
HTML代碼為:
<hello></hello>
結果渲染后的HTML為:
<hello> <div><h3>hello world</h3></div> </hello>
其中tElement是指使用此指令的元素,而tAttrs則實例的屬性,它是一個由元素上所有的屬性組成的集合(對象)形如:
{ title:‘aaaa’, name:'leifeng' }
下面讓我們看看template是一個函數時候的情況
angular.module("app",[]).directive("directitle",function(){
return{
restrict:'EAC',
template: function(tElement,tAttrs){
var _html = '';
_html += '<div>'+tAttrs.title+'</div>';
return _html;
}
};
})
HTML代碼:
<directitle title='biaoti'></directitle>
渲染之后的HTML:
<div>biaoti</div>
因為一段HTML文本,閱讀跟維護起來都是很麻煩的,所用通常會使用templateUrl這個。
(1)一個代表HTML文件路徑的字符串
(2)一個函數,可接受兩個參數tElement和tAttrs(大致同上)
由於加載html模板是通過異步加載的,若加載大量的模板會拖慢網站的速度,這里有個技巧,就是先緩存模板
你可以再你的index頁面加載好的,將下列代碼作為你頁面的一部分包含在里面。
<script type='text/ng-template' id='woshimuban.html'>
<div>我是模板內容</div>
</script>
這里的id屬性就是被設置在templateUrl上用的。
另一種辦法緩存是:
angular.module("template.html", []).run(["$templateCache", function($templateCache) {
$templateCache.put("template.html",
"<div>wo shi mu ban</div>");
}]);
(布爾值),默認值為false,設置為true時候,我們再來看看下面的例子(對比下在template時候舉的例子)
angular.module("app",[]).directive("hello",function(){
return{
restrict:'EA',
replace:true,
template:"<div><h3>hello world</h3></div>"
};
})
HTML代碼為:
<hello></hello>
渲染之后的代碼:
<div><h3>hello world</h3></div>
對比下沒有開啟replace時候的渲染出來的HTML。發現<hello></hello>不見了。
另外當模板為純文本(即template:"wo shi wen ben")的時候,渲染之后的html代碼默認的為文本用span包含。
可選參數,(布爾值或者對象)默認值為false,可能取值:
(1)默認值false。
表示繼承父作用域;
(2)true
表示繼承父作用域,並創建自己的作用域(子作用域);
(3){}
表示創建一個全新的隔離作用域;
我們都知道ng-controller(內置指令)可以從父作用域中繼承並且創建一個新的子作用域。如下:
<!doctype html> <html ng-app="myApp"> <head> <script src="http://cdn.staticfile.org/angular.js/1.2.10/angular.min.js"></script> </head> <body> <div ng-init="aaa='父親'"> parentNode:{{aaa}} <div ng-controller="myController"> chrildNode: {{aaa}} </div> </div> <script> angular.module('myApp', []) .controller('myController',function($scope){ $scope.aaa = '兒子' }) </script> </body> </html>
這時頁面顯示是
parentNode:父親
若去掉
$scope.aaa = '兒子'
則顯示
parentNode:父親
1)若一個元素上有多個指令,使用了隔離作用域,則只有其中一個可以生效;
2)只有指令模板中的根元素才能獲得一個新的作用域,這時候,scope就被設置為true了;
<!doctype html> <html ng-app="myApp"> <head> <script src="http://cdn.staticfile.org/angular.js/1.2.10/angular.min.js"></script> </head> <body> <div ng-init="aaa='父親'"> parentNode:{{aaa}} <div class='one' ng-controller="myController"> chrildNode: {{aaa}} <div class='two' ng-controller='myController2'> {{aaa}} </div> </div> </div> <script> angular.module('myApp', []) .controller('myController',function($scope){ $scope.aaa = '兒子'; }) .controller('myController2',function($scope){ $scope.aaa = '孫女'; }) </script> </body> </html>
頁面顯示為:
parentNode:父親
上面中class為one那個div獲得了指令ng-controller=’myController‘所創建的新的作用域;
而class為two那個div獲得了指令ng-controller=’myController2‘所創建的新的作用域;
這就是“只有指令模板中的根元素才能獲得一個新的作用域”;
接下來我們通過一個簡單明了的例子來說明scope取值不同的差別
<!doctype html> <html ng-app="myApp"> <head> <script src="http://cdn.staticfile.org/angular.js/1.2.10/angular.min.js"></script> </head> <body> <div ng-controller='MainController'> 父親: {{name}} <input ng-model="name" /> <div my-directive></div> </div> <script> angular.module('myApp', []) .controller('MainController', function ($scope) { $scope.name = 'leifeng'; }) .directive('myDirective', function () { return { restrict: 'EA', scope:false,//改變此處的取值,看看有什么不同 template: '<div>兒子:{{ name }}<input ng-model="name"/></div>' }; }); </script> </body> </html>
依次設置scope的值false,true,{},結果發現(大家別偷懶,動手試試哈)
當為false時候,兒子繼承父親的值,改變父親的值,兒子的值也隨之變化,反之亦如此。(繼承不隔離)
當為true時候,兒子繼承父親的值,改變父親的值,兒子的值隨之變化,但是改變兒子的值,父親的值不變。(繼承隔離)
當為{}時候,沒有繼承父親的值,所以兒子的值為空,改變任何一方的值均不能影響另一方的值。(不繼承隔離)
下面看一個例子
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://cdn.staticfile.org/angular.js/1.2.10/angular.min.js"></script>
</head>
<body>
<div ng-controller='MainController'>
<input type="text" ng-model="color" placeholder="Enter a color"/>
<hello-world></hello-world>
</div>
<script>
var app = angular.module('myApp',[]);
app.controller('MainController',function(){});
app.directive('helloWorld',function(){
return {
scope: false,
restrict: 'AE',
replace: true,
template: '<p style="background-color:{{color}}">Hello World</p>'
}
});
</script>
</body>
</html>
運行代碼,並在input中輸入顏色值,結果為

但是,但我們將scope設置為{}時候,再次運行上面的代碼可以發現頁面並不能成功完整顯示!
原因在於,這里我們將scope設置為{},產生了隔離作用域。
所以在template模板中{{color}}變成了依賴於自己的作用域,而不是依賴於父作用域。
因此我們需要一些辦法來讓隔離作用域能讀取父作用域的屬性,就是綁定策略。
下面我們就來探索設置這種綁定的幾種方法
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://cdn.staticfile.org/angular.js/1.2.10/angular.min.js"></script>
</head>
<body>
<div ng-controller='MainController'>
<input type="text" ng-model="color" placeholder="Enter a color"/>
<hello-world color-attr='{{color}}'></hello-world> //注意這里設置了color-attr屬性,綁定了{{color}}
</div>
<script>
var app = angular.module('myApp',[]);
app.controller('MainController',function(){});
app.directive('helloWorld',function(){
return {
scope: {color:'@colorAttr'}, //指明了隔離作用域中的屬性color應該綁定到屬性colorAttr
restrict: 'AE',
replace: true,
template: '<p style="background-color:{{color}}">Hello World</p>'
}
});
</script>
</body>
</html>
這種辦法只能單向,通過在運行的指令的那個html標簽上設置color-attr屬性,並且采用{{}}綁定某個模型值。
注意,你也可以再這里直接綁定字符串的顏色值,如:color-attr=“red”;
然后你可以看到表達式{{color}}被賦值給了color-attr。
當表達式的值發生變化時,屬性color-attr也會發生變化,所以也改變了隔離作用域中的屬性color。
tips:如果綁定的隔離作用域屬性名與元素的屬性名相同,則可以采取缺省寫法。
html: <hello-world color="{{color}}"/> js定義指令的片段: app.directive('helloWorld',function(){ return { scope: { color: '@' }, ... //配置的余下部分 } });
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://cdn.staticfile.org/angular.js/1.2.10/angular.min.js"></script>
</head>
<body>
<div ng-controller='MainController'>
<input type="text" ng-model="color" placeholder="Enter a color"/>
{{color}}
<hello-world color='color'></hello-world> //注意這里的寫法
</div>
<script>
var app = angular.module('myApp',[]);
app.controller('MainController',function(){});
app.directive('helloWorld',function(){
return {
scope:{color:'='},
restrict: 'AE',
replace: true,
template: '<div style="background-color:{{color}}">Hello World<div><input type="text" ng-model="color"></div></div>'
}
});
</script>
</body>
</html>
此處也類似上面采用了缺省的寫法。
這里需要注意的是,我們要直接在指令運行的那個元素上設置屬性時候,是直接將 實際的作用域模型 賦值給該屬性(這里就是color)
這樣一個雙向綁定被建立了,改變任何一個input都會改變另一個值。

<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://cdn.staticfile.org/angular.js/1.2.10/angular.min.js"></script>
</head>
<body>
<div ng-controller='MainController'>
<input type="text" ng-model="name" placeholder="Enter a color"/>
{{name}}
<hello-world saysomething999="say();" name="liucunjie"></hello-world> //注意這里
</div>
<script>
var app = angular.module('myApp',[]);
app.controller('MainController',function($scope){
$scope.say = function(){
alert('hello');
}
$scope.name = 'leifeng';
});
app.directive('helloWorld',function(){
return {
scope:{
saysomething:'&saysomething999',
name:'@'
},
restrict: 'AE',
replace: true,
template: '<button type="button" ng-bind="name" ng-init="saysomething();"></button>'
}
});
</script>
</body>
</html>
運行之后,彈出alert框。
(布爾值或者字符‘element’),默認值為false;
這個配置選項可以讓我們提取包含在指令那個元素里面的內容,再將它放置在指令模板的特定位置。
當你開啟transclude后,你就可以使用ng-transclude來指明了應該在什么地方放置transcluded內容
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://cdn.staticfile.org/angular.js/1.2.10/angular.min.js"></script>
</head>
<body>
<div ng-controller='MainController'>
<div class='a'>
<p>china</p>
<hello-world>
{{name}}
</hello-world>
</div>
</div>
<script>
var app = angular.module('myApp',[]);
app.controller('MainController',function($scope){
$scope.name = 'leifeng';
});
app.directive('helloWorld',function(){
return {
scope:{},
restrict: 'AE',
transclude: true,
template: '<div class="b"><div ng-transclude>你看不見我</div></div>'
}
});
</script>
</body>
</html>
運行上面的代碼,輸出
china
leifeng
另外當開啟transclude,會創建一個新的transclude空間,並且繼承了父作用域(即使Scope設置為隔離作用域),
上面代碼中的{{name}}是依賴於父作用域的,仍然能被渲染出來,就說明了這點。
我們再看看生成的html為下圖所示,可以發現文本“你看不見我”消失了,這是因為被transclude內容替換掉了。
這里的transclude內容就是{{name}}

接下來再來看看transclude的另一個取值transclude:“element”
那transclude:“element”與transclude:true有什么區別呢?
區別在於嵌入的內容,以上面的例子來說,
當transclude:true時候,嵌入的內容為{{name}},
而當transclude:“element”時候,嵌入的內容為
<hello-world>
{{name}}
</hello-world>
沒錯,此時嵌入的內容為整個元素。
將上面代碼transclude:true換成transclude:true后,再運行,你會發現結果並不是和你想的一樣
再次查看生成的html代碼

你會發現指令綁定的元素被轉換為了一個HTML注釋
解決方案是加上replace: true,就正常了
這時再查看HTML代碼

OK,那么現在問題來了,如果我們想把嵌入部分多次放入我們的模板中要怎么辦?
則可以使用$transclude(后面再controller選項中會講)
或者可以使用compile函數,里面有個transcludeFn參數(后面會講)
或者使用link鏈接函數。。。
可以是一個字符串或者函數。
若是為字符串,則將字符串當做是控制器的名字,來查找注冊在應用中的控制器的構造函數
angular.module('myApp', [])
.directive('myDirective', function() {
restrict: 'A', // 始終需要
controller: 'SomeController'
})
// 應用中其他的地方,可以是同一個文件或被index.html包含的另一個文件
angular.module('myApp')
.controller('SomeController', function($scope, $element, $attrs, $transclude) {
// 控制器邏輯放在這里
});
也可以直接在指令內部的定義為匿名函數,同樣我們可以再這里注入任何服務($log,$timeout等等)
angular.module('myApp',[])
.directive('myDirective', function() {
restrict: 'A',
controller:
function($scope, $element, $attrs, $transclude) {
// 控制器邏輯放在這里
}
});
另外還有一些特殊的服務(參數)可以注入
(1)$scope,與指令元素相關聯的作用域
(2)$element,當前指令對應的 元素
(3)$attrs,由當前元素的屬性組成的對象
(4)$transclude,嵌入鏈接函數,實際被執行用來克隆元素和操作DOM的函數
指令的控制器和link函數(后面會講)可以進行互換。區別在於,控制器主要是用來提供可在指令間復用的行為但link鏈接函數只能在當前內部指令中定義行為,且無法再指令間復用。
html代碼:
<my-site site="http://www.cnblogs.com/cunjieliu">雷鋒叔叔的博客</my-site> js代碼: <script> angular.module('myApp',[]).directive('mySite', function () { return { restrict: 'EA', transclude: true, //注意此處必須設置為true controller: function ($scope, $element,$attrs,$transclude,$log) { //在這里你可以注入你想注入的服務 $transclude(function (clone) { var a = angular.element('<a>'); a.attr('href', $attrs.site); a.text(clone.text()); $element.append(a); }); $log.info("hello everyone"); } }; }); </script>
運行上面的代碼就是

並且在控制台下輸出hello everyone
讓我們看看$transclude();在這里,它可以接收兩個參數,第一個是$scope,作用域,第二個是帶有參數clone的回調函數。
而這個clone實際上就是嵌入的內容(經過jquery包裝),可以在它上做很多DOM操作。
View Code
<script> angular.module('myApp',[]).directive('mySite', function () { return { restrict: 'EA', transclude: true, controller: function ($scope, $element,$attrs,$transclude,$log) { var a = $transclude(); //$transclude()就是嵌入的內容 $element.append(a); } }; }); </script>
默認情況下,如果我們簡單實用$transclude(),那么默認的其作用域就是$transclude生成的作用域
但是如果我們實用$transclude($scope,function(clone){}),那么作用域就是directive的作用域了
那么問題又來了。如果我們想實用父作用域呢
可以使用$scope.$parent
<div ng-controller='parentctrl'>
<div ng-controller='sonctrl'>
<my-site site="http://www.cnblogs.com/cunjieliu"><div>雷鋒叔叔的博客</div></my-site>
</div>
</div>
<script>
var app = angular.module('myApp',[]);
app.controller('sonctrl',function($scope){
$scope.title = 'hello son';
});
app.controller('parentctrl',function($scope){
$scope.title = 'hello parent';
});
app.directive('mySite', function () {
return {
restrict: 'EA',
transclude: true,
controller:
function ($scope, $element,$attrs,$transclude,$log) {
var a = $transclude();
$element.append(a);
$log.info($scope.title);
$log.info($scope.$parent.title);
}
};
});
</script>
這個選項的作用是可以設置你的控制器的別名
一般以前我們經常用這樣方式來寫代碼:
angular.module("app",[])
.controller("demoController",["$scope",function($scope){
$scope.title = "angualr";
}])
<div ng-app="app" ng-controller="demoController">
{{title}}
</div>
后來angularjs1.2給我們帶來新語法糖,所以我們可以這樣寫
angular.module("app",[])
.controller("demoController",[function(){
this.title = "angualr";
}])
<div ng-app="app" ng-controller="demoController as demo">
{{demo.title}}
</div>
同樣的我們也可以再指令里面也這樣寫
<script> angular.module('myApp',[]).directive('mySite', function () { return { restrict: 'EA', transclude: true, controller:'someController', controllerAs:'mainController' //..其他配置 }; }); </script>
字符串代表另一個指令的名字,它將會作為link函數的第四個參數
具體用法我們可以舉個例子說明
假設現在我們要編寫兩個指令,兩個指令中的link鏈接函數中(link函數后面會講)存在有很多重合的方法,
這時候我們就可以將這些重復的方法寫在第三個指令的controller中(上面也講到controller經常用來提供指令間的復用行為)
然后在這兩個指令中,require這個擁有controller字段的的指令(第三個指令),
最后通過link鏈接函數的第四個參數就可以引用這些重合的方法了。
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://cdn.staticfile.org/angular.js/1.2.10/angular.min.js"></script>
</head>
<body>
<outer-directive>
<inner-directive></inner-directive>
<inner-directive2></inner-directive2>
</outer-directive>
<script>
var app = angular.module('myApp', []);
app.directive('outerDirective', function() {
return {
scope: {},
restrict: 'AE',
controller: function($scope) {
this.say = function(someDirective) {
console.log('Got:' + someDirective.message);
};
}
};
});
app.directive('innerDirective', function() {
return {
scope: {},
restrict: 'AE',
require: '^outerDirective',
link: function(scope, elem, attrs, controllerInstance) {
scope.message = "Hi,leifeng";
controllerInstance.say(scope);
}
};
});
app.directive('innerDirective2', function() {
return {
scope: {},
restrict: 'AE',
require: '^outerDirective',
link: function(scope, elem, attrs, controllerInstance) {
scope.message = "Hi,shushu";
controllerInstance.say(scope);
}
};
});
</script>
</body>
</html>
上面例子中的指令innerDirective和指令innerDirective2復用了定義在指令outerDirective的controller中的方法
也進一步說明了,指令中的controller是用來讓不同指令間通信用的。
另外我們可以在require的參數值加上下面的某個前綴,這會改變查找控制器的行為:
(1)沒有前綴,指令會在自身提供的控制器中進行查找,如果找不到任何控制器,則會拋出一個error
(2)?如果在當前的指令沒有找到所需的控制器,則會將null傳給link連接函數的第四個參數
(3)^如果在當前的指令沒有找到所需的控制器,則會查找父元素的控制器
(4)?^組合
首先加載angularjs庫,查找到ng-app指令,從而找到應用的邊界,
根據ng-app划定的作用域來調用$compile服務進行編譯,
angularjs會遍歷整個HTML文檔,並根據js中指令的定義來處理在頁面上聲明的各個指令
按照指令的優先級(priority)排列,根據指令中的配置參數(template,place,transclude等)轉換DOM
然后就開始按順序執行各指令的compile函數(如果指令上有定義compile函數)對模板自身進行轉換
注意:此處的compile函數是我們指令中配置的,跟上面說的$compile服務不一樣。
每個compile函數執行完后都會返回一個link函數,所有的link函數會合成一個大的link函數
然后這個大的link函數就會被執行,主要做數據綁定,通過在DOM上注冊監聽器來動態修改scope中的數據,
或者是使用$watchs監聽 scope中的變量來修改DOM,從而建立雙向綁定等等。
若我們的指令中沒有配置compile函數,那我們配置的link函數就會運行,
她做的事情大致跟上面complie返回之后所有的link函數合成的的大的link函數差不多。
所以:在指令中compile與link選項是互斥的,如果同時設置了這兩個選項,
那么就會把compile所返回的函數當做是鏈接函數,而link選項本身就會被忽略掉
compile選項可以返回一個對象或者函數
在這里我們可以在指令和實時數據被放到DOM中之前進行DOM操作,
比如我們可以在這里進行添加或者刪除節點的DOM的操作。
所以編譯函數是負責對模板的DOM進行轉換,並且僅僅只會運行一次。
//compile函數的語法 compile:function compile(tElement,tAttrs,transclude){ return{ pre:function preLink(scope,iElement,iAttrs,controller){}, post:function postLink(scope,iElement,iAttrs,controller){} } }
對於我們編寫的大部分的指令來說,並不需要對模板進行轉換,所以大部分情況只要編寫link函數就可以了。
tips:preLink函數會在編譯階段之后,指令鏈接到子元素之前執行
類似的,postLink會在指令鏈接到子元素之后執行
這意味着,為了不破壞綁定過程,如果你需要修改DOM結構,你應該在postLink函數中來做這件事。
鏈接函數負責將作用域和DOM進行鏈接。
//link鏈接函數 link:function postLink(scope,iElement,iAttrs){}
若指令中定義有require選項,則link函數會有第四個參數,代表控制器或者所依賴的指令的控制器(上面require選項例子已有例子)
偷個懶,這個重要的東西就留着下次再講吧。。。
如果你覺得文章對你有幫助,請點個推薦吧。
