0%

angular directive

angular directive

语义化标签,复用、封装,将逻辑与dom结合在一起,即插即用,指令化即模块化与通用化

demo1 (以下全部简写)

1
<hello-world></hello-world>
1
2
3
4
5
6
7
8
var app = angular.moudle('app',[])
.directive('helloWorld',function(){
return {
restrict: 'ECMA',
replace : true,
template : '<div>hello world</div>'
}
})


这里的ECMA是声明格式,默认为EA,其分别为

E - 元素名称: <my-directive></my-directive>

A - 属性: <div my-directive></div>

C - 类名: <div class='my-directive'></div>

M - 注释: <!--directive:my-directive exp--> (很少使用,exp代表一个空格,必须存在,不然没效果)

这里的一个小坑是驼峰命名,指令若是驼峰命名,视图需写成上述样式,中间用下划线相连

replace, 是否替换掉自定义的指令, 默认是false

template是字符串模板,一般情况下都是templateUrl,毕竟代码量大了成了一坨;



他还有一个属性transclude,transclude字面意思就是嵌入,需要将你的指令内部的元素(注意不是指令的模板)嵌入到你的模板中,也就是说将它从DOM元素中获取的内容放到它发现ng-transclude 指令的地方。transclude默认为false,如果设置为true,那么就需要配合ng-transclude指令来进行使用,例如上一个例子需要在模板里面写入

1
template: '<div>hello world <span ng-transclude></span></div>'

对应的html变为

1
2
3
4
<hello-world>
<span>i'm here</span>
<span>i'm here too</span>
</hello-world>

这个属性可以理解为把hello-world标签换成我们所编写的html模板,但是标签内部内容不变。

对应视图则变为“hello world i’m here i’m here too”



绑定策略

scope:{} 代表了独立作用域;

本地作用域属性:使用@符号将本地作用域同Dom属性的值进行绑定,指令内部作用域可以使用外部作用域的变量

双向绑定:通过=可以将本地作用域上的属性同父级作用域上的属性进行双向的数据绑定。就像普通的数据绑定一样,本地属性也会反映出父数据模型上所发生的改变

父级作用域绑定: 使用&符号可以对父级作用域进行绑定,以便在其中运行函数。这就意味着这个值进行设置时会生成一个指向父级作用域的包装函数,下面请看分别的例子


1
2
3
4
5
6
7
8
9
.directive("direct",function(){
return{
restrict: 'ECMA',
template: '<div>指令中:{{ name }}</div>',
scope:{
name:'@forName'
}
}
})
1
2
<direct for-name="{{ Name }}"></direct>
<input ng-model='Name'>
1
2
3
.controller('ctrl',function($scope){
$scope.Name = '';
})

这样成功地与父控制器中的Name绑定起来了,也可以写成name:‘@’,这样就默认属性名为name了即



1
2
3
4
5
6
7
8
9
.directive("direct",function(){
return{
restrict: 'ECMA',
template: '<div>指令中:<input ng-model="Name"/></div>',
scope:{
Name:'=forName'
}
}
})
1
2
<direct for-name="Name"></direct>
<input ng-model='Name'>

这样就实现了双向数据的绑定



最后是&它的含义是对父级作用域进行绑定,并将其中的属性包装成一个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.directive("direct",function(){ 
return{
restrict: 'ECMA',
template: '<div><input ng-model="model"/>\
<button ng-click="show({name:model})">show</button></div>',
scope:{
show:'&'
}
}
})
.controller("nameController",function($scope){
$scope.showName=function(name){
alert(name);
}
});
1
<direct show="showName(name)"></direct>

这个例子中,通过模板中的ng-click触发了show函数并将一个叫做model的对象作为name参数传递了进去,而在html中,我们把show的属性值设为showName(name)。



为什么Angular要为我们提供这样一套绑定策略呢?就是因为它想让我们在为指令创建隔离作用域的同时,还能访问到父级中的属性,这就像,你在隔离作用域身上打了一个洞,然后用一条管道,把指令内部和外界的属性给连起来(绑定),并且一切的通信都只能通过这条管道来实行



compile阶段进行标签解析和变换,link阶段进行数据绑定等操作

在编译的阶段,angularJs会遍历整个的文档并根据JavaScript中指令定义来处理页面上什么的指令。在遍历的过程中,有可能一层套着一层,一直延深处遍历。一但遍历和编译完毕就会返回一个叫做模板函数的函数。在这个函数没被返回(return)之前我们可以对编译后的DOM树进行修改。通常情况下,如果设置了compile函数,说明我们希望在指令和实时数据被放到DOM中之前进行DOM操作,在这个函数中进行诸如添加和删除节点等DOM操作是安全的。



1
2
3
<hello autohello=5>
<p>小平你好</p>
</hello>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
app.directive('hello',function(){
return {
restrict:'EA',
compile : function(ele,attrs){
var tpl = ele.children().clone();
for(var i=0;i<attrs.autohello -1;i++){
ele.append(tpl.clone())
};
return function(scope,ele,attrs,ctrl){

}
}
}
})



我们写了一个DIV标签,定义了一个autohello的一个属性,属性值5.我们想把 “小平你好的这几句话”输出5遍。我们定义个一个compile 的函数,里面进行了一些DOM操作。在最后return一个闭包函数就是link函数,如果同时设置这两个选项,会将compile返回的函数当做链接函数,而link函数不起作用



compile与link函数的区别:compile函数的作用是对指令的模板进行转换,而link函数是在模型和视图之间建立关联,包括事件监听,即scope之后再链接阶段才能绑定到元素上,对于同一个指令的多个实例,compile只会执行一次,而link对于指令的每次实例都会执行一次,一般情况下,compile函数用的不多,只要编写link函数就够了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var expanderModule=angular.module('expanderModule', [])
expanderModule.directive('expander', function() {
return {
restrict : 'EA',
replace : true,
transclude : true,
scope : {
title : '=expanderTitle'
},
template : '<div>'
+ '<div class="title" ng-click="toggle()">{{title}}</div>'
+ '<div class="body" ng-show="showMe" ng-transclude></div>'
+ '</div>',
link : function(scope, element, attrs) {
scope.showMe = false;
scope.toggle = function toggle() {s
scope.showMe = !scope.showMe;
}
}
}
});
1
2
3
4
expanderModule.controller('SomeController',function($scope) {
$scope.title = '点击展开';
$scope.text = '这里是内部的内容。';
});

html代码

1
2
3
4
5
6
7
8
9
<html ng-app='expanderModule'>
<body>
<div ng-controller='SomeController'>
<expander class='expander' expander-title='title'>
{{text}}
</expander>
</div>
</body>
</html>


经常使用的一个指令例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
home.directive('compileNav',['$http','$compile',function($http,$compile){
return{
restrict: 'EA',
replace: true,
controller: 'homeController',
link : function(scope,element,attrs,ctrl){
scope.$watch(function(){
return ctrl.mainservice.NavSrc; //进行监听,若有模板存在,则通过$http进行加载,
},function(newVal){
if(newVal){
$http.get(newVal).then(function(response){
$('.main-menu').html($compile(response.data)(scope));
});
}
});
}
};
}]);