这一篇从自定义指令出发,记录了定义一个指令时影响指令行为的各种因素。
试着感受这些因素,让自己更高效地编写AngularJS应用。
Directive
先从定义一个简单的指令开始。 定义一个指令本质上是在HTML中通过元素、属性、类或注释来添加功能。
AngularJS的内置指令都是以ng
开头,如果想自定义指令,建议自定义一个前缀代表自己的命名空间。
这里我们先使用my
作为前缀:
var myApp = angular.module('myApp', [])
.directive('myDirective', function() {
return {
restrict: 'A',
replace: true,
template: 'Kavlez
'
};
})
如此一来,我们可以这样使用,注意命名是camel-case:
directive()
接受两个参数
- name:字符串,指令的名字
- factory_function:函数,指令的行为
应用启动时,以name作为该应用的标识注册factory_function返回的对象。
在factory_function中,我们可以设置一些选项来改变指令的行为。
下面记录一下定义指令时用到的选项
restrict (string)
该属性用于定义指令以什么形式被使用,这是一个可选参数,本文开头定义的指令用的也是A,其实该选项默认为A。
也就是元素(E)、属性(A)、类(C)、注释(M)
(ps:EMAC? EMACS? 挺好记哈)
比如上面定义的myDirective
,可以以任何形式调用。
-
E(元素)
-
A(属性,默认值)
-
C(类名)
-
M(注释)
<--directive:my-directive expression-->
priority (Number)
也就是优先级,默认为0。
在同一元素上声明了多个指令时,根据优先级决定哪个先被调用。
如果priority相同,则按声明顺序调用。
另外,no-repeat
是所有内置指令中优先级最高的。
terminal (Boolean)
终端? 而且还是Boolean?
被名字吓到了,其实terminal的意思是是否停止当前元素上比该指令优先级低的指令。 但是相同的优先级还是会执行。
比如,我们在my-directive
的基础上再加一个指令:
.directive('momDirective',function($rootScope){
return{
priority:3,
terminal:true
};
})
调用发现my-directive
不会生效:
template (String/Function)
至少得输出点什么吧? 但template也是可选的。
String类型时,template可以是一段HTML。
Function类型时,template是一个接受两个参数的函数,分别为:
- tElement
- tAttrs
函数返回一段字符串作为模板。
templateUrl (String/Function)
这个就和上面的template很像了,只不过这次是通过URL请求一个模板。 String类型时,templateURL自然是一个URL。 Function类型时返回一段字符串作为模板URL。
replace (Boolean/String)
默认值为false,以文章开头定义的指令为例,假设我们这样调用了指令
replace为true时,输出:
Kavlez
replace为false时,输出:
Kavlez
transclude (Boolean)
该选项默认为false,翻译过来叫'嵌入',感觉还是有些生涩。template
和scope
已经可以做很多事情了,但有一点不足。
比如在原有元素的基础上添加内容,transclude
的例子如下:
{{content}}
发现div下的hr并没有被移除,就是这样的效果。
注意不要忘了在模板中声明ng-transclude
。
scope (Boolean/Object)
默认为false,true时会从父作用域继承并创建一个自己的作用域。
而ng-controller
的作用也是从父作用域继承并创建一个新的作用域。
比如这样,离开了自己的作用域就被打回原形了:
{{content}}
{{content}}
{{content}}
{{content}}
{{content}}
.controller('ChildController', function($scope) {
$scope.content = 'from child';
})
.controller('AncestorController', function($scope) {
$scope.content = 'from ancestor';
})
但不要误解,指令嵌套并不一定会改变它的作用域。
既然true
时会从父作用域继承并创建一个自己的作用域,那么我们来试试改为false
会是什么样子:
{{ myProperty }}
{{ myProperty }}
{{ myProperty }}
.directive('myDirective', function($rootScope) {
return {
scope:false
};
})
显然,结果是三行'by my-directive'。
非true即false? naive!
其实最麻烦的还是隔离作用域,
{{内容}}
我们稍微改动一下myDirective,改为输出
。
于是我试着这样定义:
{{myDirective}}
这里需要注意的不是@
,重点是隔离作用域。
根据上面的例子输出,template中的{{myDirective}}
不会影响到其他作用域。
我们再试试这样:
{{content}}
发现大家都在一起变,也就是说值是通过复制DOM属性并传递到隔离作用域。ng-model
是个强大的指令,它将自己的隔离作用域和DOM作用域连在一起,这样就是一个双向数据绑定。
如何向指令的隔离作用域中传递数据,这里用了@
。
或者也可以写成@myDirective
,也就是说换个名字什么的也可以,比如我用@myCafe
什么的给myDirective赋值也是没问题的,总之是和DOM属性进行绑定。
另外,我们也可以用=
进行双向绑定,将本地作用域的属性同父级作用域的属性进行双向绑定。
比如下面的例子中,隔离作用域里的内容只能是'abc' :
{{content}}
在隔离作用域访问指令外部的作用域的方法还有一种,就是&
。
我们可以使用&
与父级作用域的函数进行绑定,比如下面的例子:
From
To
Content
Preview: