这一篇从自定义指令出发,记录了定义一个指令时影响指令行为的各类因素。javascript
试着感觉这些因素,让本身更高效地编写AngularJS应用。html
先从定义一个简单的指令开始。
定义一个指令本质上是在HTML中经过元素、属性、类或注释来添加功能。java
AngularJS的内置指令都是以ng
开头,若是想自定义指令,建议自定义一个前缀表明本身的命名空间。express
这里咱们先使用my
做为前缀:app
var myApp = angular.module('myApp', []) .directive('myDirective', function() { return { restrict: 'A', replace: true, template: '<p>Kavlez</p>' }; })
如此一来,咱们能够这样使用,注意命名是camel-case:函数
<my-directive /> <!-- <my-directive><p>Kavlez</p></my-directive> -->
directive()
接受两个参数spa
应用启动时,以name做为该应用的标识注册factory_function返回的对象。翻译
在factory_function中,咱们能够设置一些选项来改变指令的行为。双向绑定
下面记录一下定义指令时用到的选项rest
该属性用于定义指令以什么形式被使用,这是一个可选参数,本文开头定义的指令用的也是A,其实该选项默认为A。
也就是元素(E)、属性(A)、类(C)、注释(M)
(ps:EMAC? EMACS? 挺好记哈)
好比上面定义的myDirective
,能够以任何形式调用。
<my-directive></my-directive>
<div my-directive="expression"></div>
<div class="my-directive:expression;"></div>
<--directive:my-directive expression-->
也就是优先级,默认为0。
在同一元素上声明了多个指令时,根据优先级决定哪一个先被调用。
若是priority相同,则按声明顺序调用。
另外,no-repeat
是全部内置指令中优先级最高的。
终端? 并且仍是Boolean?
被名字吓到了,其实terminal的意思是是否中止当前元素上比该指令优先级低的指令。
可是相同的优先级仍是会执行。
好比,咱们在my-directive
的基础上再加一个指令:
.directive('momDirective',function($rootScope){ return{ priority:3, terminal:true }; })
调用发现my-directive
不会生效:
<div mom-directive my-directive="content" ></div>
至少得输出点什么吧? 但template也是可选的。
String类型时,template能够是一段HTML。
Function类型时,template是一个接受两个参数的函数,分别为:
函数返回一段字符串做为模板。
这个就和上面的template很像了,只不过此次是经过URL请求一个模板。
String类型时,templateURL天然是一个URL。
Function类型时返回一段字符串做为模板URL。
默认值为false,以文章开头定义的指令为例,假设咱们这样调用了指令
<my-directive></my-directive>
replace为true时,输出:
<p>Kavlez</p>
replace为false时,输出:
<my-directive><p>Kavlez</p></my-directive>
该选项默认为false,翻译过来叫'嵌入',感受仍是有些生涩。
template
和scope
已经能够作不少事情了,但有一点不足。
好比在原有元素的基础上添加内容,transclude
的例子以下:
<body ng-app="myApp"> <textarea ng-model="content"></textarea> <div my-directive title="Kavlez"> <hr> {{content}} </div> </body> <script type="text/javascript"> var myApp = angular.module('myApp', []) .directive('myDirective', function() { return { restrict: 'EA', scope: { title: '@', content: '=' }, transclude: true, template: '<h2 class="header">{{ title }}</h2>\ <span class="content" ng-transclude></span>' }; }); </script>
发现div下的hr并无被移除,就是这样的效果。
注意不要忘了在模板中声明ng-transclude
。
默认为false,true时会从父做用域继承并建立一个本身的做用域。
而ng-controller
的做用也是从父做用域继承并建立一个新的做用域。
好比这样,离开了本身的做用域就被打回原形了:
<div ng-init="content='from root'"> {{content}} <div ng-controller="AncestorController"> {{content}} <div ng-controller="ChildController"> {{content}} </div> {{content}} </div> {{content}} </div> .controller('ChildController', function($scope) { $scope.content = 'from child'; }) .controller('AncestorController', function($scope) { $scope.content = 'from ancestor'; })
但不要误解,指令嵌套并不必定会改变它的做用域。
既然true
时会从父做用域继承并建立一个本身的做用域,那么咱们来试试改成false
会是什么样子:
<div ng-init="myProperty='test'"> {{ myProperty }} <div my-directive ng-init="myProperty = 'by my-directive'"> {{ myProperty }} </div> {{ myProperty }} </div> .directive('myDirective', function($rootScope) { return { scope:false }; })
显然,结果是三行'by my-directive'。
非true即false? naive!
其实最麻烦的仍是隔离做用域,
咱们稍微改动一下myDirective,改成输出<p>{{内容}}</p>
。
因而我试着这样定义:
<body ng-app="myApp" > <p ng-controller="myController"> <div my-directive="I have to leave." ></div> {{myDirective}} </p> </body> <script type="text/javascript"> var myApp = angular.module('myApp', []) .directive('myDirective', function($rootScope) { $rootScope.myDirective = 'from rootScope'; return { priority:1000, restrict: 'A', replace: true, scope: { myDirective: '@', }, template: '<p>{{myDirective}}</p>' }; }) .controller('myController',function($scope){ $scope.myDirective = 'from controller'; }); </script>
这里须要注意的不是@
,重点是隔离做用域。
根据上面的例子输出,template中的{{myDirective}}
不会影响到其余做用域。
咱们再试试这样:
<input type="text" ng-model="content"> <p ng-controller="myController" > <div my-directive="{{content}}" ></div> {{content}} </p>
发现你们都在一块儿变,也就是说值是经过复制DOM属性并传递到隔离做用域。
ng-model
是个强大的指令,它将本身的隔离做用域和DOM做用域连在一块儿,这样就是一个双向数据绑定。
如何向指令的隔离做用域中传递数据,这里用了@
。
或者也能够写成@myDirective
,也就是说换个名字什么的也能够,好比我用@myCafe
什么的给myDirective赋值也是没问题的,总之是和DOM属性进行绑定。
另外,咱们也能够用=
进行双向绑定,将本地做用域的属性同父级做用域的属性进行双向绑定。
好比下面的例子中,隔离做用域里的内容只能是'abc' :
<body ng-app="myApp" ng-init="content='abc'"> <p ng-controller="myController" > <input type="text" ng-model="content"> <div my-directive="content" ></div> {{content}} </p> </body> <script type="text/javascript"> var myApp = angular.module('myApp', []) .directive('myDirective', function($rootScope) { return { priority:1000, restrict: 'A', replace: true, scope: { myDirective: '=', }, template: '<p>from myDirective:{{myDirective}}</p>' }; }) .controller('myController',function($scope){ $scope.content = 'from controller'; }); </script>
在隔离做用域访问指令外部的做用域的方法还有一种,就是&
。
咱们可使用&
与父级做用域的函数进行绑定,好比下面的例子:
<body ng-app="myApp"> <div ng-controller="myController"> <table border='1'> <tr> <td>From</td> <td><input type="text" ng-model="from"/></td> </tr> <tr> <td>To</td> <td><input type="text" ng-model="to"/></td> </tr> <tr> <td>Content</td> <td><textarea cols="30" rows="10" ng-model="content"></textarea></td> </tr> <tr> <td>Preview:</td> <td><div scope-example to="to" on-send="sendMail(content)" from="from" /></td> </tr> </table> </div> </div> </body> <script type="text/javascript"> var myApp = angular.module('myApp', []) .controller('myController',function($scope){ $scope.sendMail=function(content){ console.log('content is:::'+content); } }) .directive('scopeExample',function(){ return{ restrict:'EA', scope: { to: '=', from: '=' , send: '&onSend' }, template:'<div>From:{{from}}<br>\ To:{{to}}<br>\ <button ng-click="send()">Send</button>\ </div>' } }) </script>
控制器也能够在指令里定义,好比:
.directive('myDirective', function() { restrict: 'A', controller: 'myController' }).controller('myController', function($scope, $element, $attrs,$transclude) { //... })
相同的效果,也能够这样声明:
directive('myDirective', function() { restrict: 'A', controller:function($scope, $element, $attrs, $transclude) { //... } });
能够从名字和类型看出,这个选项是用来设置控制器的别名的。
好比这样:
directive('myDirective', function() { restrict: 'A', controller:function($scope, $element, $attrs, $transclude) { //... } });
虽然说这个东西不是很经常使用吧,但倒是值得了解的选项。
compile
和link
,这两个选项关系到AngularJS的生命周期。
先在这里简单记录一下我对生命周期的认识。
ng-repeat
之类的指令对DOM进行操做则再合适不过了。好了,接下来咱们就试试compile:
<body ng-app="myApp"> <my-directive ng-model="myName"></my-directive> </body> <script type="text/javascript"> var myApp = angular.module('myApp', []) .directive('myDirective', function($rootScope) { $rootScope.myName = 'Kavlez'; return { restrict: 'EA', compile:function(tEle, tAttrs, transcludeFn) { var h2 = angular.element('<h2></h2>'); h2.attr('type', tAttrs.type); h2.attr('ng-model', tAttrs.ngModel); h2.html("hello {{"+tAttrs.ngModel+"}}"); tEle.replaceWith(h2); } }; }); </script>
原文出处 AngularJS - 自定义指令