angularjs 指令详解
一、指令定义
对于指令,可以把它简单的理解成在特定DOM元素上运行的函数,指令可以扩展这个元素的功能。
首先来看个完整的参数示例再来详细的介绍各个参数的作用及用法:
<div my-directive></div>
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[string]
restrict是一个可选的参数。用于指定该指令在DOM中以何种形式被声明。默认值是A,即以属性的形式来进行声明。 可选值如下: E(元素) <my-directive></my-directive> A(属性,默认值) <div my-directive></div> C(类名) <div class="my-directive:expression;"></div> M(注释) <--directive:my-directive expression-->
一般考虑到浏览器的兼容性,强烈建议使用默认的属性就可以即即以属性的形式来进行声明。最后一种方式建议在不要求逼格指数的时候千万不要用。
replace[bool]
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>
templateUrl[string or function]
templateUrl是可选的参数,可以是以下类型:
- 一个代表外部HTML文件路径的字符串;
- 一个可以接受两个参数的函数,参数为tElement和tAttrs,并返回一个外部HTML文件路径的字符串。
无论哪种方式,模板的URL都将通过ng内置的安全层,特别是$getTrustedResourceUrl,这样可以保护模板不会被不信任的源加载。 默认情况下,调用指令时会在后台通过Ajax来请求HTML模板文件。加载大量的模板将严重拖慢一个客户端应用的速度。为了避免延迟,可以在部署应用之前对HTML模板进行缓存。
angular.module('app',[]) .directive('myDirective', function () { return { restrict: 'A', templateUrl: function (elem, attr) { return attr.value + ".html"; //当然这里我们可以直接指定路径,同时在模板中可以包含表达式 } }; })
controller[string or function]
controller参数可以是一个字符串或一个函数。当设置为字符串时,会以字符串的值为名字,来查找注册在应用中的控制器的构造函数.
angular.module('myApp', []) .directive('myDirective', function() { restrict: 'A',
replace: true,
templateUrl: 'test.html', 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" }
三、指令作用域
scope参数[bool or object]
scope参数是可选的,可以被设置为true或一个对象。默认值是false。
html代码
<div ng-controller='MainController' ng-init="myProperty='Hello World!'"> 外部: {{ myProperty}} <input ng-model="myProperty" /> <div my-directive></div> </div>
js代码
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>' }; });
当我们改变scope的值我们会发现
false:继承但不隔离
1.当我们将scope
设置为false
的时候,我们创建的指令和父作用域(其实是同一个作用域)共享同一个model
模型,所以在指令中修改模型数据,它会反映到父作用域的模型中。
true:继承并隔离
2.当我们将scope
设置为true
的时候,我们就新创建了一个作用域,只不过这个作用域是继承了我们的父作用域;
我觉得可以这样理解,我们新创建的作用域是一个新的作用域,只不过在初始化的时候,用了父作用域的属性和方法去填充我们这个新的作用域。它和父作用域不是同一个作用域。
{}:隔离且不继承
3.当我们将scope
设置为{}
时,意味着我们创建的一个新的与父作用域隔离的新的作用域,这使我们在不知道外部环境的情况下,就可以正常工作,不依赖外部环境。
四、绑定策略
在使用独立作用域scope的时候,一般有三种绑定传递策略, @单向传递字符串 =双向传递 &单向传递父级的方法
<inputtype="text"ng-model="myUrl"> <div my-directive my-url="{{myUrl}}" my-age="age" change-my-age="changeAge()></div> ①
angular.module('myApp',[]) .directive('myDirective',function(){ return{ restrict:'A',//属性方式 replace:true, scope:{ myUrl:'@',//@绑定策略(默认绑定到 my-url指令属性) myAge:'='//=双向绑定(父子互相影响)
changeMyAge:'&' //传递父作用域的方法
}, template:'<a href="{{myUrl}}" ng-click=changeMyAge()>{{ myAge }}</a>' } });
-
在上面的代码中,我创建了一个指令
myDirective
该指令创建了两个变量 myUrl、myLinkText,并且这俩个变量都是采用@绑定策略
。 -
说一下,不管是
@
、=
还是&
绑定策略,它们都有一个默认的方式,以@绑定策略为例,如上面代码那么样:myUrl:'@'
,直接用一个@
表示绑定的方式,它就会默认得将指令属性my-url的值赋值给myUrl变量。 -
当然,你不想使用默认的方式,也就是说,你不想myUrl变量绑定my-url的值,而想要绑定其它属性名的值,那么你可以在
@
后加上你希望的属性名(格式要求:驼峰式)。 -
如,我想讲myUrl绑定到
<myDirective></myDirective>
指令的some-attr属性的值,那么你可以这样写:myUrl:'@someAttr'
。 -
那么我们知道了指令的myUrl变量的值是如何来的,那么我们要如何在template中使用它呢?
-
这个很简单,看上面的代码就能很明白了,我们在template中的代码中需要用表达式的方式对其引用
{{myUrl}}
,这样我们就能够使用到myUrl变量的值了~
1. 本地作用域属性:使用@符号将本地作用域同DOM属性的值进行绑定,使指令内部作用域可以使用外部作用域的变量:
@
可以在指令中使用绑定的字符串了。
2. 双向绑定:通过=可以将本地作用域上的属性同父级作用域上的属性进行双向的数据绑定。就像普通的数据绑定一样,本地属性会反映出父数据模型中所发生的改变。
3. 父级作用域绑定 通过&符号可以对父级作用域进行绑定,以便在其中运行函数。意味着对这个值进行设置时会生成一个指向父级作用域的包装函数。
要使调用带有一个参数的父方法,我们需要传递一个对象,这个对象的键是参数的名称,值是要传递给参数的内容。