AngularJS中除了內置指令,還能夠自定義指令。自定義指令和自定義過濾器同樣,有兩種方法:
javascript
第一種,在module中配置:$compileProvider.directive('directiveName', function(){ });css
代碼模版爲:html
$compileProvider.directive('', ['', function(){ // Runs during compile return { // name: '', // priority: 1, // terminal: true, // scope: {}, // {} = isolate, true = child, false/undefined = no change // controller: function($scope, $element, $attrs, $transclude) {}, // require: 'ngModel', // Array = multiple requires, ? = optional, ^ = check parent elements // restrict: 'A', // E = Element, A = Attribute, C = Class, M = Comment // template: '', // templateUrl: '', // replace: true, // transclude: true, // compile: function(tElement, tAttrs, function transclude(function(scope, cloneLinkingFn){ return function linking(scope, elm, attrs){}})), link: function($scope, iElm, iAttrs, controller) { } };
第二種,.directive('directiveName', function(){ });java
代碼模版爲:
app
.directive('', ['', function(){ // Runs during compile return { // name: '', // priority: 1, // terminal: true, // scope: {}, // {} = isolate, true = child, false/undefined = no change // controller: function($scope, $element, $attrs, $transclude) {}, // require: 'ngModel', // Array = multiple requires, ? = optional, ^ = check parent elements // restrict: 'A', // E = Element, A = Attribute, C = Class, M = Comment // template: '', // templateUrl: '', // replace: true, // transclude: true, // compile: function(tElement, tAttrs, function transclude(function(scope, cloneLinkingFn){ return function linking(scope, elm, attrs){}})), link: function($scope, iElm, iAttrs, controller) { } }; }]);
能夠看到,定義指令會返回一個對象,這個對象裏面包含了各個屬性(選項),這些屬性(選項)就是用來定義指令的。ide
指令的名字不要和內置指令衝突,若是指令的名字爲xxx-yyy,那麼設置指令的名字時應爲xxxYyy,即駝峯式的命名。函數
restrict: 描述指令在模版中的使用方式,包括:元素、樣式類、屬性、註釋,或者以上幾種方式的任意組合。post
template: 以字符串的形式編寫一個內聯模板。ui
templateUrl: 加載模版所須要使用的url,若是已經指定了template,此屬性會被忽略。this
replace: 若是該屬性爲true,則替換指令所在的元素;若是爲false或者不指定,則追加到元素內部。
例子:
<!DOCTYPE html> <html ng-app="firstMoudule"> <head> <meta charset='utf-8'> </head> <body ng-controller="firstController"> <!-- 使用自定義指令first-tag --> <div first-tag></div> <script src="http://cdn.bootcss.com/angular.js/1.4.0-rc.2/angular.min.js"></script> <script type="text/javascript"> angular.module('firstMoudule', [], function($compileProvider, $controllerProvider) { $compileProvider.directive('firstTag', function() { return { restrict: 'A', // E = Element, A = Attribute, C = Class, M = Comment template: '<div>hello pomelo!</div>', replace: true }; }); $controllerProvider.register('firstController', function() {}); }); </script> </body> </html>
transclude: 當此屬性爲true時,把指令元素中原來的子節點移動到一個新模板的內部。
例子:
<!DOCTYPE html> <html ng-app="firstMoudule"> <head> <meta charset='utf-8'> </head> <body ng-controller="firstController"> <div first-tag> old data </div> <script src="http://cdn.bootcss.com/angular.js/1.4.0-rc.2/angular.min.js"></script> <script type="text/javascript"> angular.module('firstMoudule', [], function($compileProvider, $controllerProvider) { $compileProvider.directive('firstTag', function() { return { restrict: 'A', // E = Element, A = Attribute, C = Class, M = Comment /*transclude爲true時,old data會被放到具備ng-transclude屬性的地方,也就是下面的span*/ template: '<div>new data <span ng-transclude></span> </div>', replace: true, transclude: true }; }); $controllerProvider.register('firstController', function() {}); }); </script> </body> </html>
輸出
priority: 設置指令在模板中的優先級,用整數來表示,數字大的優先級高,先執行。執行順序是相對於元素上的其它指令而言的。若是兩個指令的該值相同,則先定義的先執行。好比內置的ng-repeat該值爲1000。
terminal: 和priority配合使用。若是此屬性爲true,那麼priority比它小的都不會再執行。
例子:
<!DOCTYPE html> <html ng-app="firstMoudule"> <head> <meta charset='utf-8'> </head> <body ng-controller="firstController"> <!-- 同時使用兩個指令 --> <div first-tag second-tag></div> <script src="http://cdn.bootcss.com/angular.js/1.4.0-rc.2/angular.min.js"></script> <script type="text/javascript"> angular.module('firstMoudule', [], function($compileProvider, $controllerProvider) { $compileProvider.directive('firstTag', function() { return { restrict: 'A', priority: 10 }; }); $compileProvider.directive('secondTag', function() { return { template: '<div>data</div>', replace: true, transclude: true, priority: 20, terminal: true }; }); $controllerProvider.register('firstController', function() {}); }); </script> </body> </html>
注意,這裏同時使用兩個指令,只能有一個裏面template有內容,不然將出錯。second-tag優先級較高,先執行,而且terminal爲true,first-tag不會執行。
complie、link:雖然template的方式頗有用,但對於指令來講,真正有趣的發生在complie和link函數中。這兩個函數是根據Angular建立動態視圖的兩個處理階段來命名的。Angular的初始化過程爲:
1.加載腳本 加載Angular庫,查找ng-app指令,從而找到應用的邊界。
2.編譯階段 遍歷DOM結構,標識出模版中註冊的全部指令。對於每一條指令,若是存在complie函數,則調用complie函數獲得一個編譯好的template函數,template函數又會調用從全部指令收集來的link函數。編譯階段就是負責模板的轉換。
3.連接階段 爲了讓視圖變成動態的,Angular會對每一條指令運行一個link函數。link函數負責在model和view之間進行動態關聯。
complie函數僅僅在編譯階段運行一次,而link函數對於指令的每個實例,都會執行一次。
對於咱們會編寫的大多數指令來講,並不須要對模板轉換,只有編寫link函數便可。有complie函數就不用再定義link函數了。
complie函數的語法爲:
這裏返回的至關於link函數。
compile: function(tElement, tAttrs,transclude) { return { pre: function preLink() { }, post: function postLink() { } }; }
tElement是當前指令所在的jQuery對象。tAttrs是指令上定義的參數,好比指令fisrt-tag="123",則tAttrs爲123 。這裏transclude是一個函數,若是須要對內容進行變換,而簡單的基於模板的變換並無提供這種功能,那麼能夠本身寫這個函數。
若是直接返回,則返回的是postLink,以下:
compile: function(tElement, tAttrs,transclude) { return function() { }; }
preLink在編譯階段以後,指令連接子元素以前運行。postLink在全部的子元素指令都連接後才運行。若是須要修改DOM結構,應該在postLink裏面作這件事情,若是在preLink裏面作則會破壞綁定過程,並致使錯誤。
例子
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body ng-app="app"> <div ng-controller="Controller1"> <div tag1 tag2></div> </div> <script src="http://cdn.bootcss.com/angular.js/1.4.0-rc.2/angular.min.js"></script> <script type="text/javascript"> angular.module('app', [], function($compileProvider) { $compileProvider.directive('tag1', function() { return { restrict: 'A', template: '<div>hello pomelo!</div>', replace: true, compile: function(tElement, tAttrs, transclude) { console.log('tag1 complie...'); return { pre: function preLink() { console.log('tag1 preLink...'); }, post: function postLink() { console.log('tag1 postLink...'); } }; } }; }); $compileProvider.directive('tag2', function() { return { restrict: 'A', replace: false, compile: function(tElement, tAttrs, transclude) { console.log('tag2 complie...'); return { pre: function preLink() { console.log('tag2 preLink...'); }, post: function postLink() { console.log('tag2 postLink...'); } }; } }; }); }) .controller('Controller1', function() {}); </script> </body> </html>
controller、controllerAs、require:controller會暴露一個API,經過這個API能夠在多個指令之間經過依賴注入進行通訊。controllerAs是給controller起一個別名,方便使用。require能夠將其它指令傳遞給本身。
例子:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body ng-app="app"> <div ng-controller="Controller1"> <div tag1></div> </div> <script src="http://cdn.bootcss.com/angular.js/1.4.0-rc.2/angular.min.js"></script> <script type="text/javascript"> angular.module('app', [], function($compileProvider) { $compileProvider.directive('tag1', function() { return { restrict: 'A', controller: function($scope) { $scope.data = 'this is the data in controller'; this.Data = 'some Data'; }, controllerlAs: 'Controller', link: function($scope, iElm, iAttrs, Controller) { console.log($scope.data); console.log(Controller.Data); } }; }); }) .controller('Controller1', function() {}); </script> </body> </html>
在多個指令間通訊還須要require方法。
例子:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body ng-app="app"> <div ng-controller="Controller1"> <div parent-tag></div> </div> <script src="http://cdn.bootcss.com/angular.js/1.4.0-rc.2/angular.min.js"></script> <script type="text/javascript"> angular.module('app', []) .directive('parentTag', function() { return { restrict: 'ECMA', template: '<div><ul><li ng-repeat="i in players">`i`.`name` `i`.`number`</li></ul><child-tag></child-tag></div>', replace: true, controller: function($scope) { $scope.players = [{ name: 'Mertersacker', number: 4 }, { name: 'Koscielny', number: 6 }, { name: 'Gabriel', number: 5 }]; this.addPlayer = function() { $scope.$apply(function() { $scope.players.push({ name: 'Chambers', number: 21 }); }); } }, controllerAs: 'parentController' }; }) .directive('childTag', function() { return { restrict: 'ECMA', require: '^parentTag', template: '<button>add player Chambers</button>', replace: true, link: function($scope, iElm, iAttrs, parentController) { iElm.on('click', parentController.addPlayer); } } }) .controller('Controller1', function() {}); </script> </body> </html>
scope:指明指令所操控數據的做用域。
若是不指定,scope爲false,會使用指令對應的DOM元素上存在的scope對象;
若是scope爲true,則會建立一個scope,它繼承了外層控制器中的scope,在繼承樹中,位於當前scope對象上方的全部scope對象的值均可以被讀取。
若是scope爲{attributeName:'bindingStratry',... ...}即一個對象時,會建立一個獨立的對象。
對於scope是一個object的狀況,有點複雜。此時scope的結構爲:
scope:{
attributeName1:'&bindingStratry1',
attributeName2:'=bindingStratry2',
attributeName3:'@bindingStratry3'
}
當爲&bindingStratry時,表示傳遞一個來自父scope的函數,稍後調用。
例子:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body ng-app="app"> <div ng-controller="Controller1"> <div my-tag obj="players"></div> </div> <script src="http://cdn.bootcss.com/angular.js/1.4.0-rc.2/angular.min.js"></script> <script type="text/javascript"> angular.module('app', []) .directive('myTag', function() { return { restrict: 'ECMA', controller: function($scope) { console.log($scope.myScopeFn());//myScopeFn必須以函數方法使用 }, scope: { myScopeFn: '&obj' } } }) .controller('Controller1', function($scope) { $scope.players = [{ name: 'Mertersacker', number: 4 }, { name: 'Koscielny', number: 6 }, { name: 'Gabriel', number: 5 }]; }); </script> </body> </html>
當爲=bindingStratry時,表示傳遞一個來自父scope的屬性,而且是和父scope中對應屬性雙向綁定的。
例子:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body ng-app="app"> <div ng-controller="Controller1"> <div my-tag obj="players"></div> </div> <script src="http://cdn.bootcss.com/angular.js/1.4.0-rc.2/angular.min.js"></script> <script type="text/javascript"> angular.module('app', []) .directive('myTag', function() { return { restrict: 'ECMA', controller: function($scope) { <!-- 雙向數據綁定,能夠操縱父scope的數據,這裏添加一個元素進去 --> $scope.myScopeAttr.push({ name: 'Ozil', number: 11 }); }, scope: { myScopeAttr: '=obj' } } }) .controller('Controller1', function($scope) { $scope.players = [{ name: 'Mertersacker', number: 4 }, { name: 'Koscielny', number: 6 }, { name: 'Gabriel', number: 5 }]; console.log($scope.players); }); </script> </body> </html>
能夠看到,父scope中players的數據改變了。=bindingStratry能雙向數據綁定。
當爲@bindingStratry時,表示讀取一個來自父scope的屬性,這個屬性只讀,沒法改變。
例子:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body ng-app="app"> <div ng-controller="Controller1"> <!-- 這裏要特別注意,要放在{{ }}裏 --> <div my-tag obj="`players`"></div> </div> <script src="http://cdn.bootcss.com/angular.js/1.4.0-rc.2/angular.min.js"></script> <script type="text/javascript"> angular.module('app', []) .directive('myTag', function() { return { restrict: 'ECMA', controller: function($scope) { console.log($scope.myScopeAttr); }, scope: { myScopeAttr: '@obj' } } }) .controller('Controller1', function($scope) { $scope.players = [{ name: 'Mertersacker', number: 4 }, { name: 'Koscielny', number: 6 }, { name: 'Gabriel', number: 5 }]; }); </script> </body> </html>
值得一提的是,@bindingStratry是把當前屬性做爲一個字符串傳遞,因此對象等引用類型傳過來會變成字符串,最好仍是傳字符串類型的數據過來。