angularJS directive详解
前言
最近学习了下angularjs指令的相关知识,也参考了前人的一些文章,在此总结下。
欢迎批评指出错误的地方。
Angularjs指令定义的API
AngularJs的指令定义大致如下
angular.module("app",[]).directive("directiveName",function(){
return{
//通过设置项来定义
};
})
其中return返回的对象包含很多参数,下面一一说明
1.restrict
(字符串)可选参数,指明指令在DOM里面以什么形式被声明;
取值有:E(元素),A(属性),C(类),M(注释),其中默认值为A;
E(元素):<directiveName></directiveName>
A(属性):<div directiveName='expression'></div>
C(类): <div class='directiveName'></div>
M(注释):<--directive:directiveName expression-->
例如restrict:‘EA’ 则表示指令在DOM里面可用元素形式和属性形式被声明;
一般来说,当你创建一个有自己模板的组件的时候,需要使用元素名,如果仅仅是为为已有元素添加功能的话,就使用属性名
注意:如果想支持IE8,则最好使用属性和类形式来定义。 另外Angular从1.3.x开始, 已经放弃支持IE8了.
2.priority
(数字),可选参数,指明指令的优先级,若在单个DOM上有多个指令,则优先级高的先执行;
设置指令的优先级算是不常用的
比较特殊的的例子是,angularjs内置指令的ng-repeat的优先级为1000,ng-init的优先级为450;
3.terminal
(布尔型),可选参数,可以被设置为true或false,若设置为true,则优先级低于此指令的其他指令则无效,不会被调用(优先级相同的还是会执行)
4.template(字符串或者函数)可选参数,可以是:
(1)一段HTML文本
angular.module("app",[]).directive("hello",function(){
return{
restrict:'EA',
template:"<div><h3>hello world</h3></div>"
};
})
HTML代码为:<hello></hello>
结果渲染后的HTML为:<hello>
<div><h3>hello world</h3></div>
</hello>
(2)一个函数,可接受两个参数tElement和tAttrs
其中tElement是指使用此指令的元素,而tAttrs则实例的属性,它是一个由元素上所有的属性组成的集合(对象)形如:
{
title:‘aaaa’,
name:'leifeng'
}
下面让我们看看template是一个函数时候的情况
angular.module("app",[]).directive("directitle",function(){
return{
restrict:'EAC',
template: function(tElement,tAttrs){
var _html = '';
_html += '<div>'+tAttrs.title+'</div>';
return _html;
}
};
})
HTML代码:<directitle title='biaoti'></directitle>
渲染之后的HTML:<div>biaoti</div>
因为一段HTML文本,阅读跟维护起来都是很麻烦的,所用通常会使用templateUrl这个。
5.templateUrl(字符串或者函数),可选参数,可以是
(1)一个代表HTML文件路径的字符串
(2)一个函数,可接受两个参数tElement和tAttrs(大致同上)
注意:在本地开发时候,需要运行一个服务器,不然使用templateUrl会报错 Cross Origin Request Script(CORS)错误
由于加载html模板是通过异步加载的,若加载大量的模板会拖慢网站的速度,这里有个技巧,就是先缓存模板
你可以再你的index页面加载好的,将下列代码作为你页面的一部分包含在里面。
<script type='text/ng-template' id='woshimuban.html'>
<div>我是模板内容</div>
</script>
这里的id属性就是被设置在templateUrl上用的。
另一种办法缓存是:
angular.module("template.html", []).run(["$templateCache", function($templateCache) {
$templateCache.put("template.html",
"<div>wo shi mu ban</div>");
}]);
6.replace
(布尔值),默认值为false,设置为true时候,我们再来看看下面的例子(对比下在template时候举的例子)
angular.module("app",[]).directive("hello",function(){
return{
restrict:'EA',
replace:true,
template:"<div><h3>hello world</h3></div>"
};
})
HTML代码为:
<hello></hello>
渲染之后的代码:<div><h3>hello world</h3></div>
对比下没有开启replace时候的渲染出来的HTML。发现<hello></hello>不见了。
另外当模板为纯文本(即template:"wo shi wen ben")的时候,渲染之后的html代码默认的为文本用span包含。
7.scope
可选参数,(布尔值或者对象)默认值为false,可能取值:
(1)默认值false。
表示继承父作用域;
(2)true
表示继承父作用域,并创建自己的作用域(子作用域);
(3){}
表示创建一个全新的隔离作用域;
7.1首先我们先来了解下scope的继承机制。我们用ng-controller这个指令举例,
我们都知道ng-controller(内置指令)可以从父作用域中继承并且创建一个新的子作用域。如下:
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://cdn.staticfile.org/angular.js/1.2.10/angular.min.js"></script>
</head>
<body>
<div ng-init="aaa='父亲'">
parentNode:{{aaa}}
<div ng-controller="myController">
chrildNode: {{aaa}}
</div>
</div>
<script>
angular.module('myApp', [])
.controller('myController',function($scope){
$scope.aaa = '儿子'
})
</script>
</body>
</html>
这时页面显示是
parentNode:父亲
chrildNode: 儿子
若去掉
$scope.aaa = '儿子'
则显示
parentNode:父亲
chrildNode: 父亲
注意:
1)若一个元素上有多个指令,使用了隔离作用域,则只有其中一个可以生效;
2)只有指令模板中的根元素才能获得一个新的作用域,这时候,scope就被设置为true了;
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://cdn.staticfile.org/angular.js/1.2.10/angular.min.js"></script>
</head>
<body>
<div ng-init="aaa='父亲'">
parentNode:{{aaa}}
<div class='one' ng-controller="myController">
chrildNode: {{aaa}}
<div class='two' ng-controller='myController2'>
{{aaa}}
</div>
</div>
</div>
<script>
angular.module('myApp', [])
.controller('myController',function($scope){
$scope.aaa = '儿子';
})
.controller('myController2',function($scope){
$scope.aaa = '孙女';
})
</script>
</body>
</html>
页面显示为:
parentNode:父亲
chrildNode: cunjieliu
孙女
上面中class为one那个div获得了指令ng-controller=’myController‘所创建的新的作用域;
而class为two那个div获得了指令ng-controller=’myController2‘所创建的新的作用域;
这就是“只有指令模板中的根元素才能获得一个新的作用域”;
接下来我们通过一个简单明了的例子来说明scope取值不同的差别
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://cdn.staticfile.org/angular.js/1.2.10/angular.min.js"></script>
</head>
<body>
<div ng-controller='MainController'>
父亲: {{name}}
<input ng-model="name" />
<div my-directive></div>
</div>
<script>
angular.module('myApp', [])
.controller('MainController', function ($scope) {
$scope.name = 'leifeng';
})
.directive('myDirective', function () {
return {
restrict: 'EA',
scope:false,//改变此处的取值,看看有什么不同
template: '<div>儿子:{{ name }}<input ng-model="name"/></div>'
};
});
</script>
</body>
</html>
依次设置scope的值false,true,{},结果发现(大家别偷懒,动手试试哈)
当为false时候,儿子继承父亲的值,改变父亲的值,儿子的值也随之变化,反之亦如此。(继承不隔离)
当为true时候,儿子继承父亲的值,改变父亲的值,儿子的值随之变化,但是改变儿子的值,父亲的值不变。(继承隔离)
当为{}时候,没有继承父亲的值,所以儿子的值为空,改变任何一方的值均不能影响另一方的值。(不继承隔离)
tip:当你想要创建一个可重用的组件时隔离作用域是一个很好的选择,通过隔离作用域我们确保指令是‘独立’的,并可以轻松地插入到任何HTML app中,并且这种做法防止了父作用域被污染;
7.2隔离作用域可以通过绑定策略来访问父作用域的属性。
下面看一个例子
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://cdn.staticfile.org/angular.js/1.2.10/angular.min.js"></script>
</head>
<body>
<div ng-controller='MainController'>
<input type="text" ng-model="color" placeholder="Enter a color"/>
<hello-world></hello-world>
</div>
<script>
var app = angular.module('myApp',[]);
app.controller('MainController',function(){});
app.directive('helloWorld',function(){
return {
scope: false,
restrict: 'AE',
replace: true,
template: '<p style="Hello World</p>'
}
});
</script>
</body>
</html>
运行代码,并在input中输入颜色值,结果为
但是,但我们将scope设置为{}时候,再次运行上面的代码可以发现页面并不能成功完整显示!
原因在于,这里我们将scope设置为{},产生了隔离作用域。
所以在template模板中{{color}}变成了依赖于自己的作用域,而不是依赖于父作用域。
因此我们需要一些办法来让隔离作用域能读取父作用域的属性,就是绑定策略。
下面我们就来探索设置这种绑定的几种方法
方法一:使用@(@attr)来进行单向文本(字符串)绑定
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://cdn.staticfile.org/angular.js/1.2.10/angular.min.js"></script>
</head>
<body>
<div ng-controller='MainController'>
<input type="text" ng-model="color" placeholder="Enter a color"/>
<hello-world color-attr='{{color}}'></hello-world> //注意这里设置了color-attr属性,绑定了{{color}}
</div>
<script>
var app = angular.module('myApp',[]);
app.controller('MainController',function