转自:http://www.cnblogs.com/rohelm/p/4051437.htmlhtml
对于指令,能够把它简单的理解成在特定DOM元素上运行的函数,指令能够扩展这个元素的功能。express
首先来看个完整的参数示例再来详细的介绍各个参数的做用及用法:数组
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是一个可选的参数。用于指定该指令在DOM中以何种形式被声明。默认值是A,即以属性的形式来进行声明。
可选值以下:
E(元素)浏览器
<my-directive></my-directive>
A(属性,默认值)缓存
<div my-directive="expression"></div>
C(类名)安全
<div class="my-directive:expression;"></div>
M(注释)app
<--directive:my-directive expression-->
通常考虑到浏览器的兼容性,强烈建议使用默认的属性就能够即即以属性的形式来进行声明。最后一种方式建议再不要求逼格指数的时候千万不要用。ide
Code:函数
angular.module('app',[]) .directive('myDirective', function () { return { restrict: 'E', template: '<a href="http://www.baidu.com">百度</a>' }; })
HtmlCode:
<my-directive></my-directive>
效果:post
大多数指令会忽略这个参数,使用默认值0,但也有些场景设置高优先级是很是重要甚至是必须的。例如,ngRepeat将这个参数设置为1000,这样就能够保证在同一元素上,它老是在其余指令以前被调用。
这个参数用来中止运行当前元素上比本指令优先级低的指令。但同当前指令优先级相同的指令仍是会被执行。
例如:ngIf的优先级略高于ngView(它们操控的实际就是terminal参数),若是ngIf的表达式值为true,ngView就能够被正常执行,但若是ngIf表达式的值为false,因为ngView的优先级较低就不会被执行。
template参数是可选的,必须被设置为如下两种形式之一:
首先演示下第二种用法:
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是可选的参数,能够是如下类型:
不管哪一种方式,模板的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是一个可选参数,若是设置了这个参数,值必须为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参数是可选的,能够被设置为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>
JsCode:
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是一个可选的参数。默认值是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
<div class="alert"> <a href class="close" ng-click="close()">×</a> <div ng-transclude></div> </div>
index.html
<div ng-controller="Controller"> <my-dialog ng-hide="dialogIsHidden" on-close="hideDialog()"> Check out the contents, {{name}}! </my-dialog> </div>
若是指令使用了transclude参数,那么在控制器没法正常监听数据模型的变化了。建议在连接函数里使用$watch服务。
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">谷歌</div>
仅在compile参数中使用transcludeFn是推荐的作法。link函数能够将指令互相隔离开来,而controller则定义可复用的行为。若是咱们但愿将当前指令的API暴露给其余指令使用,可使用controller参数,不然可使用link来构造当前指令元素的功能性(即内部功能)。若是咱们使用了scope.$watch()或者想要与DOM元素作实时的交互,使用连接会是更好的选择。使用了嵌入,控制器中的做用域所反映的做用域可能与咱们所指望的不同,这种状况下,$scope对象没法保证能够被正常更新。当想要同当前屏幕上的做用域交互时,可使用传入到link函数中的scope参数。
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为字符串表明另一个指令的名字。require会将控制器注入到其所指定的指令中,并做为当前指令的连接函数的第四个参数。字符串或数组元素的值是会在当前指令的做用域中使用的指令名称。在任何状况下,ng编译器在查找子控制器时都会参考当前指令的模板。
compile选项自己并不会被频繁使用,可是link函数则会被常用。本质上,当咱们设置了link选项,其实是建立了一个postLink() 连接函数,以便compile() 函数能够定义连接函数。一般状况下,若是设置了compile函数,说明咱们但愿在指令和实时数据被放到DOM中以前进行DOM操做,在这个函数中进行诸如添加和删除节点等DOM操做是安全的。
compile和link选项是互斥的。若是同时设置了这两个选项,那么会把compile所返回的函数看成连接函数,而link选项自己则会被忽略。
编译函数负责对模板DOM进行转换。连接函数负责将做用域和DOM进行连接。 在做用域同DOM连接以前能够手动操做DOM。在实践中,编写自定义指令时这种操做是很是罕见的,但有几个内置指令提供了这样的功能。
compile: function(tEle, tAttrs, transcludeFn) { //todo: return function(scope, ele, attrs) { // 连接函数 };
连接函数是可选的。若是定义了编译函数,它会返回连接函数,所以当两个函数都定义时,编译函数会重载连接函数。若是咱们的指令很简单,而且不须要额外的设置,能够从工厂函数(回调函数)返回一个函数来代替对象。若是这样作了,这个函数就是连接函数。
它提供更底层的API来处理控制器内的数据,这个API用来处理数据绑定、验证、 CSS更新等不实际操做DOM的事情,ngModel 控制器会随 ngModel 被一直注入到指令中,其中包含了一些方法。为了访问ngModelController必须使用require设置.
ngModelController经常使用的元素以下:
$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); }); } }); }); } }; });