去看原文
在某些时候,angular内置的指令无法满足我们需求的时候,可能 需要我们自定义指令来解决。下面是angular指令的各项参数说明。可以作为手册使用
//angular指令的定义,myDirective ,使用驼峰命名法
angular.module('myApp', [])
.directive('myDirective', function ($timeout, UserDefinedService) {
// 指令操作代码放在这里
});
//angular自定义指令 的使用,使用 “-” 来代替驼峰命名法
//<div my-directive></div>
//注意:为了避免与未来的HTML标准冲突,给自定义的指令加入前缀来代表自定义的命名空间。AngularJS本身已经使用了 ng- 前缀,所以可以选择除此以外的名字。在例子中我们使用 my- 前缀(比如 my-derictive ) 。
//指令的生命周期开始于 $compile 方法并结束于 link 方法
//自定义指令的全部可设置的属性大致如下
//指令的选项如下所示,每个键的值说明了可以将这个属性设置为何种类型或者什么样的函数:
angular.module('myApp', [])
.directive('myDirective', function() { //指令名称myDirective
return { //返回一个对象
restrict: String, //可选字符串参数,可以设置这个指令在DOM中可以何种形式被声明,
//默认为A(attr(当做标签属性来使用))<div my-directive></div>
// 设置为“E”(ele,(直接当做标签来使用)) <my-directive></my-directive>
//C(类名)
//<div class="my-directive:expression;"></div>
//M(注释)
//<--directive:my-directive expression-->
//这些选项可以单独使用,也可以混合在一起使用:
//angular.module('myDirective', function(){
// return {
// restrict: 'EA' // 输入元素或属性
// };
//})
priority: Number, //优先级,可忽略,默认为0, ngRepeat的优先级为1000,这样就可以保证在同一元素上,它总是在其他指令之前被调用。
terminal: Boolean,//(布尔型),true或false,如果为false,则这个参数用来告诉AngularJS停止运行当前元素上比本指令优先级低的指令。优先级相同的指令还是会被执行。 ngIf 的优先级略高于 ngView ,
template: String //or Template Function: //(字符串或函数)指令中的一个重要的一个属性,必须被设置其中一种
//1, 一段HTML文本;
//2,可以接收两个参数的函数,参数为 tElement 和 tAttrs
//在html模板中必须只有一个根html标签,且如果有换行则需要使用“\”
//例如template: '\
// <div> <-- single root element -->\
// <a href="http://google.com">Click me</a>\
// <h1>When using two elements, wrap them in a parent element</h1>\
// </div>\
//function(tElement, tAttrs) (...},
//更好的选择是使用 templateUrl 参数引用外部模板,参考下面的参数
templateUrl: String, //(字符串或函数)1,外部路径的字符串,2,接受两个参数的函数,参数为 tElement 和 tAttrs ,并返回一个外部HTML文件路径的字符串
//模板加载后,AngularJS会将它默认缓存到 $templateCache 服务中。(可以提前加载模块到缓存中,提高加载速度)
replace: Boolean, //or String, //(布尔型)默认为false(模板内容会加载到标签内部),true(模板内容会替换当前标签)
scope: Boolean, //or Object, //(布尔型或对象),默认为false, 设置为true 时,会从父作用域继承并创建一个新的作用域对象。设置为空对象,有个好处,指令在多个地方使用时,不会相互影响。
// ng-controller 的作用,就是从父级作用域继承并创建一个新的子作用域。
//如果要创建一个能够从外部原型继承作用域的指令,将 scope 属性设置为 true
//设置为一个对象,则能设置 隔离作用域, scope 属性设置为一个空对象 {} 。如果这样做了,指令的模板就无法访问外部作用域了:
//例如.directive('myDirective', function() {
// return {
// restrict: 'A',
// scope: {},
// priority: 100,
// template: '<div>Inside myDirective {{ myProperty }}</div>'
// };
// });
//在scope对象中,还可以使用“@” “=” “&”,来设置模板中数据的作用域和绑定规则
//"@" 本地作用域属性:使用当前指令中的数据和DOM属性的值进行绑定
//“=” 双向绑定:本地作用域上的属性同父级作用域上的属性进行双向的数据绑定。
//“&” 父级作用域绑定:通过 & 符号可以对父级作用域进行绑定
//例如
//scope: {
// ngModel: '=', // 将ngModel同指定对象绑定
// onSend: '&', // 将引用传递给这个方法
// fromName: '@' // 储存与fromName相关联的字符串
//}
transclude: Boolean, //默认为false.只有当你希望创建一个可以包含任意内容的指令时, 才使用 transclude: true 。
//如果指令使用了 transclude 参数,那么在控制器(下面马上会介绍)中就无法正常监听数
//据模型的变化了。
controller: String //or function(scope, element, attrs, transclude, otherInjectables) { ... }, //(字符串或函数)注册在应用中的控制器的构造函数
//使用函数创建内联控制器,例如
//angular.module('myApp',[])
// .directive('myDirective', function() {
// restrict: 'A',
// controller:
function($scope, $element, $attrs, $transclude) {
// // 控制器逻辑放在这里
// }
//})
controllerAs: 'String', //可以在指令中创建匿名控制器,例如
//.directive('myDirective', function() {
// return {
// restrict: 'A',
// template: '<h4>{{ myController.msg }}</h4>',
// controllerAs: 'myController',
// controller: function() {
// this.msg = "Hello World"
// }
// };
//});
require: 'String', //(字符串或数组)字符串代表另外一个指令的名字,如果没有前缀,指令将会在自身所提供的控制器中进行查找,如果没有找到任何控制器(或
//具有指定名字的指令)就抛出一个错误。
//例如
//如果不使用 ^ 前缀,指令只会在自身的元素上查找控制器。
//require: 'ngModel'
// 使用 ? 如果在当前指令中没有找到所需要的控制器,会将 null 作为传给 link 函数的第四个参数
//require: '?ngModel'
//使用 ^ 如果添加了 ^ 前缀,指令会在上游的指令链中查找 require 参数所指定的控制器。
//require: '^ngModel'
// 使用 ^? 将前面两个选项的行为组合起来,我们可选择地加载需要的指令并在父指令链中进行查找。
//require: '^?ngModel',
link: function(scope, iElement, iAttrs) { ... }, //compile 选项本身并不会被频繁使用,但是 link 函数则会被经常使用。
//当我们设置了 link 选项, 实际上是创建了一个 postLink() 链接函数, 以便 compile() 函数可以定义链接函数。
//compile 和 link 选项是互斥的。如果同时设置了这两个选项,那么会把 compile
//所返回的函数当作链接函数,而 link 选项本身则会被忽略。
//通常情况下,如果设置了 compile 函数,说明我们希望在指令和实时数据被放到DOM中之前
//进行DOM操作,在这个函数中进行诸如添加和删除节点等DOM操作是安全的。
//用 link 函数创建可以操作DOM的指令。
//require 'SomeController',
//link: function(scope, element, attrs, SomeController) {
// 在这里操作DOM,可以访问required指定的控制器
//}
compile: function(tElement, tAttrs, transclude) {
return {
pre: function(scope, iElement, iAttrs, controller) { ... },
post: function(scope, iElement, iAttrs, controller) { ... }
}
// 或者
return function postLink(...) { ... }
}
};
});
然后详细解释一下scope的几种使用方法:
scope的值有三种:true、false、{}
scope = false
在这种情况下指令模板中可以直接使用父作用域的变量:
html部分
<div ng-controller="myCtrl" class="row">
<div class="col-md-3">
<input class="form-control" type="text" ng-model="ctrlFlavor">
<drink flavor="{{ ctrlFlavor }}"></drink>
</div>
</div>
js部分
var module = angular.module('myModule',[]);
module.controller('myCtrl',function($scope){
$scope.ctrlFlavor = "百威";
});
module.directive('drink',function(){
return {
restrict:'AE',
scope:false,
template:"<input class='form-control' ng-model='ctrlFlavor'>"
}
})
效果如下,两个表单同步更新
scope=false
html部分和上面一致,
js部分将scope的值给为false。
此时再看效果如下
可以看到在通过directive内部在修改变量ctrlFlavor之前,会保持和父作用域变量一致。也就是依然是继承了父作用域变量,不同之处在于此时是创建了一个新的作用域,然后把父作用域的值全部拿了过来。
scope={}
而很多时候我们需要在任意地方来使用一个指令,也就是我们不希望这个指令需要依赖父级作用域的变量或不希望它会受父级作用域的影响(当然,有些时候我们也需要使用父级作用域的变量,这在scope={}情况下依然可以实现,后面会有解释),这个时候我们就可以给scope赋值一个空对象,即scope= {}
此时以为这我们创建了一个新的与父级作用与隔离的作用域,这是我们可以在不知道外部环境的情况下使用指令。
对于参数为空对象的情况不多解释,下面具体学习一下对象的几中属性各自的使用。
在创建了隔离的作用域,我们可以通过@,&,=引用应用指令的元素的属性,如这行代码,我们可以在<div class="my-directive" my-directive my-name="{{name}}" age="age" change-my-age="changeAge()"></div>这个元素中,利用前缀标识符通过使用属性my-name,age,change-my-age来引用这些属性的值。
下面来看一下@,&,=这三种标识符如何使用:
-
@
单向绑定的前缀标识符
使用方法::在元素中使用属性,就像这样<div my-directive my-name="{{ name }}"></div>,注意,属性名字要用-将两个单词连接,因为数据的单向绑定所以要通过使用{{}}来绑定数据。这里也就是把父作用域的$scope.name和指令作用域的my-name绑定
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="../framework/bootstrap.min.css">
<script src="../framework/angular.js"></script>
</head>
<body ng-app="myModule">
<div ng-controller="myCtrl" class="row">
<div class="col-md-3">
<input type="text" class="form-control" ng-model="name">
<my-directive my-name="{{ name }}"></my-directive>
</div>
</div><script> var module = angular.module('myModule',[]); module.controller('myCtrl',function($scope){ $scope.name = "百威"; }); module.directive('myDirective',function(){ return { restrict:'AE', scope:{ name:'@myName' //myName就是原来元素中的my-name属性,这样my-directive内的name就和父作用域的name绑定了 }, template:"<input class='form-control' ng-model='name'>" } }) </script> </body> </html>
执行效果:
可以看到修改指令内数据不会对父级数据有影响,但是修改父级数据会同步修改指令内数据
-
=
双向数据绑定前缀标识符
使用方法:在元素中使用属性,好比这样<div my-directive age="age"></div>,注意,数据的双向绑定要通过=前缀标识符实现,所以不可以使用{{}}。
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="../framework/bootstrap.min.css">
<script src="../framework/angular.js"></script>
</head>
<body ng-app="myModule">
<div ng-controller="myCtrl" class="row">
<div class="col-md-3">
<label for="">父作用域</label><input type="text" class="form-control" ng-model="age">
<label for="">指令作用域</label><my-directive my-name="age"></my-directive>
</div>
</div><script> var module = angular.module('myModule',[]); module.controller('myCtrl',function($scope){ $scope.age = 11; }); module.directive('myDirective',function(){ return { restrict:'AE', scope:{ myName:'=' //myName就是原来元素中的my-name属性,这样my-directive内的myName就和父作用域的age绑定了 }, template:"<input class='form-control' ng-model='myName'>" } }) </script> </body> </html>
与前面代码的不同之处在指令的属性改为了age,且age属性的值没有使用{{}}绑定,而是直接写上的,scope内的age值为=。此时效果就变成了双向绑定,指令外与指令内age变量同步改变,互相影响。效果如下
可以看到两个作用域的值相互影响,同步更新。
- &
绑定函数方法的前缀标识符
使用方法:
在元素中使用属性,好比这样<div my-directive change-my-age="changeAge()"></div>,注意,属性的名字要用-将多个单词连接。
指令scope中这样写:
scope:{
changeAge:"&changeMyAge"
}
进一步说明,我们的指令是如何利用这些前缀标识符来寻找我们想要的属性或者函数的?
@ 当指令编译到模板的name时,就会到scope中寻找是否含有name的键值对,如果存在,就像上面那样,看到@就知道这是一个单向的数据绑定,然后寻找原来的那个使用指令的元素上(或者是指令元素本身)含有这个值的属性即my-name={{name}},然后在父作用域查找{{name}}的值,得到之后传递给模板中的name。
=和&与@差不多,只不过=进行的是双向的数据绑定,不论模板还是父作用域上的属性的值发生改变都会使另一个值发生改变,而&是绑定函数而已。
网友评论