AngularJS中除了内置指令,还可以自定义指令。自定义指令和自定义过滤器一样,有两种方法:
第一种,在module中配置:$compileProvider.directive('directiveName', function(){ });
代码模版为:
<code> </code><code>$compileProvider.directive('', ['', function(){</code>
<code> </code><code>// Runs during compile</code>
<code> </code><code>return {</code>
<code> </code><code>// name: '',</code>
<code> </code><code>// priority: 1,</code>
<code> </code><code>// terminal: true,</code>
<code> </code><code>// scope: {}, // {} = isolate, true = child, false/undefined = no change</code>
<code> </code><code>// controller: function($scope, $element, $attrs, $transclude) {},</code>
<code> </code><code>// require: 'ngModel', // Array = multiple requires, ? = optional, ^ = check parent elements</code>
<code> </code><code>// restrict: 'A', // E = Element, A = Attribute, C = Class, M = Comment</code>
<code> </code><code>// template: '',</code>
<code> </code><code>// templateUrl: '',</code>
<code> </code><code>// replace: true,</code>
<code> </code><code>// transclude: true,</code>
<code> </code><code>// compile: function(tElement, tAttrs, function transclude(function(scope, cloneLinkingFn){ return function linking(scope, elm, attrs){}})),</code>
<code> </code><code>link: function($scope, iElm, iAttrs, controller) {</code>
<code> </code>
<code> </code><code>}</code>
<code> </code><code>};</code>
第二种,.directive('directiveName', function(){ });
<code> </code><code>.directive('', ['', function(){</code>
<code> </code><code>// Runs during compile</code>
<code> </code><code>return {</code>
<code> </code><code>// name: '',</code>
<code> </code><code>// priority: 1,</code>
<code> </code><code>// terminal: true,</code>
<code> </code><code>// scope: {}, // {} = isolate, true = child, false/undefined = no change</code>
<code> </code><code>// controller: function($scope, $element, $attrs, $transclude) {},</code>
<code> </code><code>// require: 'ngModel', // Array = multiple requires, ? = optional, ^ = check parent elements</code>
<code> </code><code>// restrict: 'A', // E = Element, A = Attribute, C = Class, M = Comment</code>
<code> </code><code>// template: '',</code>
<code> </code><code>// templateUrl: '',</code>
<code> </code><code>// replace: true,</code>
<code> </code><code>// transclude: true,</code>
<code> </code><code>// compile: function(tElement, tAttrs, function transclude(function(scope, cloneLinkingFn){ return function linking(scope, elm, attrs){}})),</code>
<code> </code><code>link: function($scope, iElm, iAttrs, controller) {</code>
<code> </code>
<code> </code><code>}</code>
<code> </code><code>};</code>
<code> </code><code>}]);</code>
可以看到,定义指令会返回一个对象,这个对象里面包含了各个属性(选项),这些属性(选项)就是用来定义指令的。
指令的名字不要和内置指令冲突,如果指令的名字为xxx-yyy,那么设置指令的名字时应为xxxYyy,即驼峰式的命名。
restrict: 描述指令在模版中的使用方式,包括:元素、样式类、属性、注释,或者以上几种方式的任意组合。
<a href="http://s3.51cto.com/wyfs02/M01/6F/2B/wKiom1WThHaAx7B6AANtxICdXuk880.bmp" target="_blank"></a>
template: 以字符串的形式编写一个内联模板。
templateUrl: 加载模版所需要使用的url,如果已经指定了template,此属性会被忽略。
replace: 如果该属性为true,则替换指令所在的元素;如果为false或者不指定,则追加到元素内部。
例子:
<code><!DOCTYPE html></code>
<code><</code><code>html</code> <code>ng-app</code><code>=</code><code>"firstMoudule"</code><code>></code>
<code><</code><code>head</code><code>></code>
<code> </code><code><</code><code>meta</code> <code>charset</code><code>=</code><code>'utf-8'</code><code>></code>
<code></</code><code>head</code><code>></code>
<code><</code><code>body</code> <code>ng-controller</code><code>=</code><code>"firstController"</code><code>></code>
<code> </code><code><!-- 使用自定义指令first-tag --></code>
<code> </code><code><</code><code>div</code> <code>first-tag></</code><code>div</code><code>></code>
<code> </code>
<code> </code><code><</code><code>script</code> <code>src</code><code>=</code><code>"http://cdn.bootcss.com/angular.js/1.4.0-rc.2/angular.min.js"</code><code>></</code><code>script</code><code>></code>
<code> </code><code><</code><code>script</code> <code>type</code><code>=</code><code>"text/javascript"</code><code>></code>
<code> </code><code>angular.module('firstMoudule', [], function($compileProvider, $controllerProvider) {</code>
<code> </code><code>$compileProvider.directive('firstTag', function() {</code>
<code> </code><code>restrict: 'A', // E = Element, A = Attribute, C = Class, M = Comment</code>
<code> </code><code>template: '<</code><code>div</code><code>>hello pomelo!</</code><code>div</code><code>>',</code>
<code> </code><code>replace: true</code>
<code> </code><code>});</code>
<code> </code><code>$controllerProvider.register('firstController', function() {});</code>
<code> </code><code>});</code>
<code> </code><code></</code><code>script</code><code>></code>
<code></</code><code>body</code><code>></code>
<code></</code><code>html</code><code>></code>
transclude: 当此属性为true时,把指令元素中原来的子节点移动到一个新模板的内部。
<code> </code><code><</code><code>div</code> <code>first-tag></code>
<code> </code><code>old data </code>
<code> </code><code></</code><code>div</code><code>></code>
<code> </code><code>/*transclude为true时,old data会被放到具有ng-transclude属性的地方,也就是下面的span*/</code>
<code> </code><code>template: '<</code><code>div</code><code>>new data <</code><code>span</code> <code>ng-transclude></</code><code>span</code><code>> </</code><code>div</code><code>>',</code>
<code> </code><code>replace: true,</code>
<code> </code><code>transclude: true</code>
输出
<a href="http://s3.51cto.com/wyfs02/M01/6F/29/wKioL1WTlxazSiMIAAAPg6Gm0FI926.jpg" target="_blank"></a>
priority: 设置指令在模板中的优先级,用整数来表示,数字大的优先级高,先执行。执行顺序是相对于元素上的其它指令而言的。如果两个指令的该值相同,则先定义的先执行。比如内置的ng-repeat该值为1000。
terminal: 和priority配合使用。如果此属性为true,那么priority比它小的都不会再执行。
<code> </code><code><!-- 同时使用两个指令 --></code>
<code> </code><code><</code><code>div</code> <code>first-tag second-tag></</code><code>div</code><code>></code>
<code> </code><code>restrict: 'A',</code>
<code> </code><code>priority: 10</code>
<code> </code><code>$compileProvider.directive('secondTag', function() {</code>
<code> </code><code>template: '<</code><code>div</code><code>>data</</code><code>div</code><code>>',</code>
<code> </code><code>transclude: true,</code>
<code> </code><code>priority: 20,</code>
<code> </code><code>terminal: true</code>
注意,这里同时使用两个指令,只能有一个里面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函数。
<code>compile: function(tElement, tAttrs,transclude) {</code>
<code> </code><code>return {</code>
<code> </code><code>pre: function preLink() { },</code>
<code> </code><code>post: function postLink() { }</code>
<code> </code><code>};</code>
<code>}</code>
tElement是当前指令所在的jQuery对象。tAttrs是指令上定义的参数,比如指令fisrt-tag="123",则tAttrs为123 。这里transclude是一个函数,如果需要对内容进行变换,而简单的基于模板的变换并没有提供这种功能,那么可以自己写这个函数。
如果直接返回,则返回的是postLink,如下:
<code> </code><code>return function() { };</code>
preLink在编译阶段之后,指令链接子元素之前运行。postLink在所有的子元素指令都链接后才运行。如果需要修改DOM结构,应该在postLink里面做这件事情,如果在preLink里面做则会破坏绑定过程,并导致错误。
例子
<code><</code><code>html</code><code>></code>
<code> </code><code><</code><code>meta</code> <code>charset</code><code>=</code><code>"utf-8"</code><code>></code>
<code><</code><code>body</code> <code>ng-app</code><code>=</code><code>"app"</code><code>></code>
<code> </code><code><</code><code>div</code> <code>ng-controller</code><code>=</code><code>"Controller1"</code><code>></code>
<code> </code><code><</code><code>div</code> <code>tag1 tag2></</code><code>div</code><code>></code>
<code> </code><code>angular.module('app', [], function($compileProvider) {</code>
<code> </code><code>$compileProvider.directive('tag1', function() {</code>
<code> </code><code>return {</code>
<code> </code><code>restrict: 'A',</code>
<code> </code><code>template: '<</code><code>div</code><code>>hello pomelo!</</code><code>div</code><code>>',</code>
<code> </code><code>replace: true,</code>
<code> </code><code>compile: function(tElement, tAttrs, transclude) {</code>
<code> </code><code>console.log('tag1 complie...');</code>
<code> </code><code>return {</code>
<code> </code><code>pre: function preLink() {</code>
<code> </code><code>console.log('tag1 preLink...');</code>
<code> </code><code>},</code>
<code> </code><code>post: function postLink() {</code>
<code> </code><code>console.log('tag1 postLink...');</code>
<code> </code><code>}</code>
<code> </code><code>};</code>
<code> </code><code>}</code>
<code> </code><code>};</code>
<code> </code><code>});</code>
<code> </code><code>$compileProvider.directive('tag2', function() {</code>
<code> </code><code>replace: false,</code>
<code> </code><code>console.log('tag2 complie...');</code>
<code> </code><code>console.log('tag2 preLink...');</code>
<code> </code><code>console.log('tag2 postLink...');</code>
<code> </code><code>})</code>
<code> </code><code>.controller('Controller1', function() {});</code>
<a href="http://s3.51cto.com/wyfs02/M00/6F/2D/wKiom1WTql7gkvnqAAA6leZ1J9I581.jpg" target="_blank"></a>
controller、controllerAs、require:controller会暴露一个API,通过这个API可以在多个指令之间通过依赖注入进行通信。controllerAs是给controller起一个别名,方便使用。require可以将其它指令传递给自己。
<code> </code><code><</code><code>div</code> <code>tag1></</code><code>div</code><code>></code>
<code> </code><code>controller: function($scope) {</code>
<code> </code><code>$scope.data = 'this is the data in controller';</code>
<code> </code><code>this.Data = 'some Data';</code>
<code> </code><code>},</code>
<code> </code><code>controllerlAs: 'Controller',</code>
<code> </code><code>link: function($scope, iElm, iAttrs, Controller) {</code>
<code> </code><code>console.log($scope.data);</code>
<code> </code><code>console.log(Controller.Data);</code>
<a href="http://s3.51cto.com/wyfs02/M01/6F/32/wKiom1WUlwTAwSXPAAA17Qx8XO0858.jpg" target="_blank"></a>
在多个指令间通信还需要require方法。
<a href="http://s3.51cto.com/wyfs02/M01/6F/2F/wKioL1WUmf7DWC2TAARVnCycsyg274.bmp" target="_blank"></a>
<code> </code><code><</code><code>div</code> <code>parent-tag></</code><code>div</code><code>></code>
<code> </code><code>angular.module('app', [])</code>
<code> </code><code>.directive('parentTag', function() {</code>
<code> </code><code>restrict: 'ECMA',</code>
<code> </code><code>template: '<</code><code>div</code><code>><</code><code>ul</code><code>><</code><code>li</code> <code>ng-repeat</code><code>=</code><code>"i in players"</code><code>>`i`.`name` `i`.`number`</</code><code>li</code><code>></</code><code>ul</code><code>><</code><code>child-tag</code><code>></</code><code>child-tag</code><code>></</code><code>div</code><code>>',</code>
<code> </code><code>controller: function($scope) {</code>
<code> </code><code>$scope.players = [{</code>
<code> </code><code>name: 'Mertersacker',</code>
<code> </code><code>number: 4</code>
<code> </code><code>}, {</code>
<code> </code><code>name: 'Koscielny',</code>
<code> </code><code>number: 6</code>
<code> </code><code>name: 'Gabriel',</code>
<code> </code><code>number: 5</code>
<code> </code><code>}];</code>
<code> </code><code>this.addPlayer = function() {</code>
<code> </code><code>$scope.$apply(function() {</code>
<code> </code><code>$scope.players.push({</code>
<code> </code><code>name: 'Chambers',</code>
<code> </code><code>number: 21</code>
<code> </code><code>});</code>
<code> </code><code>});</code>
<code> </code><code>},</code>
<code> </code><code>controllerAs: 'parentController'</code>
<code> </code><code>.directive('childTag', function() {</code>
<code> </code><code>require: '^parentTag',</code>
<code> </code><code>template: '<</code><code>button</code><code>>add player Chambers</</code><code>button</code><code>>',</code>
<code> </code><code>link: function($scope, iElm, iAttrs, parentController) {</code>
<code> </code><code>iElm.on('click', parentController.addPlayer);</code>
<code> </code><code>}</code>
<a href="http://s3.51cto.com/wyfs02/M01/6F/33/wKiom1WUoaegncVvAABZmdV8hKE121.jpg" target="_blank"></a>
<a href="http://s3.51cto.com/wyfs02/M02/6F/30/wKioL1WUo23CU0CRAACA_qq9IZI359.jpg" target="_blank"></a>
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的函数,稍后调用。
<code> </code><code><</code><code>div</code> <code>my-tag </code><code>obj</code><code>=</code><code>"players"</code><code>></</code><code>div</code><code>></code>
<code> </code><code>.directive('myTag', function() {</code>
<code> </code><code>console.log($scope.myScopeFn());//myScopeFn必须以函数方法使用</code>
<code> </code><code>scope: {</code>
<code> </code><code>myScopeFn: '&obj'</code>
<code> </code><code>.controller('Controller1', function($scope) {</code>
<code> </code><code>$scope.players = [{</code>
<code> </code><code>name: 'Mertersacker',</code>
<code> </code><code>number: 4</code>
<code> </code><code>}, {</code>
<code> </code><code>name: 'Koscielny',</code>
<code> </code><code>number: 6</code>
<code> </code><code>name: 'Gabriel',</code>
<code> </code><code>number: 5</code>
<code> </code><code>}];</code>
<a href="http://s3.51cto.com/wyfs02/M02/6F/35/wKiom1WUxCyQzkN5AACFMOeG70Q919.jpg" target="_blank"></a>
当为=bindingStratry时,表示传递一个来自父scope的属性,并且是和父scope中对应属性双向绑定的。
<code> </code>
<code> </code><code><!-- 双向数据绑定,可以操纵父scope的数据,这里添加一个元素进去 --></code>
<code> </code><code>$scope.myScopeAttr.push({</code>
<code> </code><code>name: 'Ozil',</code>
<code> </code><code>number: 11</code>
<code> </code><code>});</code>
<code> </code><code>myScopeAttr: '=obj'</code>
<code> </code><code>console.log($scope.players);</code>
<a href="http://s3.51cto.com/wyfs02/M01/6F/36/wKiom1WUy2ywC1cuAACd61NMQnE554.jpg" target="_blank"></a>
可以看到,父scope中players的数据改变了。=bindingStratry能双向数据绑定。
当为@bindingStratry时,表示读取一个来自父scope的属性,这个属性只读,无法改变。
<code> </code><code><!-- 这里要特别注意,要放在{{ }}里 --></code>
<code> </code><code><</code><code>div</code> <code>my-tag </code><code>obj</code><code>=</code><code>"`players`"</code><code>></</code><code>div</code><code>></code>
<code> </code><code>console.log($scope.myScopeAttr);</code>
<code> </code><code>myScopeAttr: '@obj'</code>
<a href="http://s3.51cto.com/wyfs02/M00/6F/34/wKioL1WU0h-Dwg-tAAA0WDfMl9g890.jpg" target="_blank"></a>
值得一提的是,@bindingStratry是把当前属性作为一个字符串传递,所以对象等引用类型传过来会变成字符串,最好还是传字符串类型的数据过来。
本文转自 iampomelo 51CTO博客,原文链接:http://blog.51cto.com/iampomelo/1669747,如需转载请自行联系原作者