angularJS自定义指令
指令
可以简单理解成特定DOM元素上执行的函数,它可以拓展元素的功能。
一个定义指令的基本格式如下:
angular.module('myApp', [])
.directive('myDirective', function(){
return {
//指令的配置项
}
});
name
指令的名字,如: myDirective。在DOM中可以这样使用: ` `;或者 ``;
定义指令时的名称用驼峰命名,使用时用中划线方式。
function
这个函数可以返回一个对象,如上面;或者直接返回一个函数,它会被接收成 postLink 函数。
指令的返回函数的所有配置项
先看看指令的所有参数配置:
angular.module('myApp', [])
.directive('myDirective', function(){
return {
restrict: string,//指令在DOM中的声明形式字符串,默认值是‘A’
proiority: number,//优先级,默认值0
terminal: boolean,//是否停止运行当前元素比本指令优先级低的指令
template: string or function,//模板 或 返回模板的函数
templateUrl: string,//模板文件的链接
replace: boolean or string,//true为替换,默认值false为插入到指令元素内部
scope: boolean or object,//指令作用域
transclude: boolean,//嵌入
controller: string or function(scope, element, attrs, transcluude, otherInjectables){},
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(){}
}
}
});
1. restrict
可选值: E(元素,用于独立的指令单元,意图表达更明确)
A(属性,默认值, 最常用,兼容性最好)
C(类名)
M(注释,尽量避免使用)
<-- directive: my-directive expression -->
2. priority
默认值是0;优先级高的指令总是优先执行。优先级相同的指令,先声明的先执行。
3. terminal
设置为true,则停止运行当前元素上比本指令优先级低的指令,但优先级相同的仍然会执行。
4. template
值可以是以下类型:
a. 模板字符串 b. 一个可以接受两个参数的函数,并返回一个代表模板的字符串
5. templateUrl
值可以是以下类型:
a. 外部html文件路径的字符串 b. 一个可以接受两个参数的函数,并返回一个外部HTML文件路径的字符串
6. replace
默认值false. 值为 true 时,模板会被当作子元素插入到调用此指令的元素内部
7. scope
可选值: false: 默认值,直接调用相同的作用域对象; true: 从当前作用域对象继承一个新的作用域对象; {}[object]: 创建一个同当前作用域相隔离的作用域对象。
隔离作用域
具有隔离作用域的指令最主要的使用场景是创建可复用的组件,组件可以在未知上下文中使用,并且可以避免污染所处的外部作用域或不经意地污染内部作用域。
绑定策略
使用了隔离作用域后,指令的模板就无法访问外部作用域了。但使用无数据的隔离作用域并不常见。AngularJS 有三种方法可以将指令内部的隔离作用域同外部作用域进行数据绑定。
本地作用域属性:
@ (or @attr)
使用@符号将本地作用域同DOM属性的值进行绑定。指令内部作用域可以使用外部作用域的变量。js
myApp.controller('myController', ['$scope', function($scope){ $scope.btnText = '按钮'; $scope.validate= '1234'; $scope.submit = function(err){ console.log(err); } }]); myApp.directive('my-btn', function(){ return { restrict: 'EA', scope: { text: '@btnText'//key是在template里使用的变量,val是在使用指令时所绑定的attr。若@符号后不指定名称,则默认绑定为键名'text' }, replace: true, template: ''//这里的{{text}}正是scope的text. } });
html
双向绑定:
= (or =attr)
可以将本地作用域上的属性同父级作用域上的属性进行双向的数据绑定。例: js,controller同上一例子,如果理解上面的例子,这里也是很好理解的,只是绑定的不是字符串,而是双向数据绑定。
myApp.directive('myInput', function(){ return { restrict: 'A', scope:{ val: '=value'//key为template里的数据模型,'value'是html里的attr. }, template: '' } });
html
父级作用域绑定:
& (or &attr)
可以对父级作用域进行绑定,可以调用父方法。意味着对这个值进行设置时会生成一个指向父级作用域的包装函数。js
myApp.directive('myError', function(){ return { restrict: 'A', scope: { validate: '&check' }, template: ''+ ''//传递参数需要传递一个对象,name是参数名,text是参数值。 } });
html
//因为是隔离作用域,他们之间的值是不会互相影响的,只是共用父方法。要调用一个带有参数的父方法,需要传递一个对象,这个对象的key是参数名称,val是要传递给参数的内容
8. transclude
transclude是一个可选参数,默认值是false, 当值为true时,angularJS会将从DOM元素中获取的内容放到它发现ng-transclude指令的地方,如:
- first
- second
angular.module('myApp', [])
.directive('sidebox', function(){
return {
scope: {
title: '@'
},
transclude: true,
template: ''+
''
}
});
只有希望创建一个可以包含任意内容的指令时,才使用transclude: true。,典型的例子是模态框或导航栏。
9. controller
值可以是一个字符串或一个函数。 string: 当设置为字符中时,指令会查找注册在应用中的同名控制器。 function: 直接通过匿名构造函数的方式来定义一个内联的控制器。
控制器主要是用来提供可以指令间复用的行为。可以的将当前指令的API暴露给其他的指令使用。如:
angular.modue('myApp', [])
.directive('inputForm', function(){
return {
restrict: 'EA',
controller: function($scope){
$scope.input = '手机号';
this.submit = function(){//通过this暴露接口
//do something...
}
}
}
});
任意的服务同样可以被传递给controller。其中有一些特殊的服务:
- $scope 与指令元素相关联的当前作用域
- $element 当前指令对应的元素
- $attrs 当前元素的属性组成的对象
- $transclude transclude链接函数是实际被执行用来克隆元素和操作DOM的函数
10. controllerAs
controllerAs用来设置控制器别名,以此为名发布控制器,并且作用域可以访问controllerAs。这样可以在视图中引用控制器,甚至无需注入$scope。例:
angular.module('myApp', [])
.directive('myDirective', function(){
return {
restrict: 'A',
template: '{{myController.msg}}
',//可以在tpl中直接访问controller
controllerAs: 'myController',
contrller: function(){
this.msg = 'Hello';
}
}
});
又例:
angular.module('myApp', [])
.controller('myController', function(){
this.msg = 'robin';
});
{{main.robin}}
11. require
其值可以是字符串或数组,字符串或数组元素代表另个一个指令的名字。require 会将指令控制器注入到当前的指令中,并作为当前指令的链接函数的第四个参数。
结合9.controller的例子,可以这样使用
angular.module('myApp', [])
.directive('otherForm', function(){
return {
restrict: 'A',
require: '^?inputForm',
link: function(scope, element, attrs, ctrl){
ctrl.submit();
}
}
});
如果是数组:
angular.module('myApp', [])
.directive('otherForm', function(){
return {
restrict: 'A',
require: [?^inputForm, inputForm2],
link: function(scope, element, attrs, ctrl){
ctrl[0].submit();
ctrl[1].api();
}
}
});
require前缀:
- ‘?’ 如果在当前指令中没有找到所面要的控制器,会将null作为link的第四个参数;
- ‘^’ 指令会在上游的指令链中查找require参数所指定的控制器;
- ‘?^’ 前两个选项的组合;
- 没有前缀 指令将在同级作用域查找所需控制器,如果没有找到会抛出一个错误。
12. compile
compile选项可以返回一个对象或函数。
compile函数可以用来在指令和实时数据被放到DOM中之前进行DOM操作,在这里进行添加和删除节点等DOM操作是安全的。
compile与link函数是互斥的。如果同时设置,compile的返回函数会被当作链接函数,link函数会被忽略。
compile: function(tEle, tAttrs, transcludeFn){
//...tEle代表未绑定事件的DOM元素
}
13. link
link函数会经常被使用。负责设置事件监听器、监听数据变化和实时的操作DOM。
如果指令很简单,可以直接从工厂函数返回一个函数来代替。如:
angular.module('myApp', [])
.directive('myDirective', function(){
return {
pre: function(tElement, tAttrs, transclude){},
post: function(scope, iElement, iAttrs, controller){}
}
});
angular.module('myApp', [])
.directive('myDirective', function(){
return {
link: function(scope, ele, attrs){
return {
pre: function(tElement, tAttrs, transclude){},
post: function(scope, iElement, iAttrs, controller){}
}
}
}
});