浅析AngularJS中的指令


Posted in Javascript onMarch 20, 2016

指令(Directives)是所有AngularJS应用最重要的部分。尽管AngularJS已经提供了非常丰富的指令,但还是经常需要创建应用特定的指令。

说到AngularJS,我们首先想到的大概也就是双向数据绑定和指令系统了,这两者也是AngularJS中最为吸引人的地方。双向数据绑定呢,感觉没什么好说的,那么今天我们就来简单的讨论下AngularJS这个框架的指令系统,本人也是初学,查阅了一些资料,要是有一些说的不好的地方,万望指出。

指令作为AngularJS中最为重要的部分,所以这个框架本身也是自带了比较多的的指令,但是在开发中,这些指令通常不能满足我们的需要,所以我们也是需要自定义一些指令的。那么一个AngularJS指令在HTML代码中可以有四种表现形式:

1、作为一个新的HTML元素来使用。

<hello></hello>或者<hello/>

2、作为一个元素的属性来使用

<div hello></div>

3、作为一个元素的类来使用

<div class="hello"></div>

4、作为注释来使用

<!--directive: hello -->

注意这里有一个陷阱,就是在“directive: hello”这个的后面要有一个空格,不然的话是没有效果的,同时推荐注释的方法的还是少用,如果非要追求高大上,那就随性吧。既然指令有以上四种表现形式,那么具体他是怎么来定义的呢?

.directive('hello',function(){
return {
restrict:'AECM',
template:'<button>click me</button>'
}
})

上面就是定义一个指令最简单的代码了,没有之一。在上面的代码中,directive()这个方法定义了一个新的指令,该方法有两个参数,第一个'hello'就是规定指令的名字为hello,第二个参数是返回指令对象的函数。那么在上面的代码中,该函数主要使用了两个属性来定义这个hello指令:

1、restrict[string]这个属性,主要是用来规定指令在HTML代码中可以使用什么表现形式。A代表属性、E代表元素、C代表类、M代表注释。实际情况中我们一般使用AE这两种方式。

2、template[string or function]这个属性,规定了指令被Angular编译和链接(link)后生成的HTML标记,这个属性可以简单到只有一个HTML文本在里面,也可以特别复杂,当该属性的值为function的时候,那么该方法返回的就是代表模板的字符串,同时也可以在里面使用{{}}这个表达式。

template: function () {
return '<button>click me</button>';
}

但是在一般情况下,template这个属性都会被templateUrl取代掉,用它来指向一个外部的文件地址,所以我们通常把模板放在外部的一个HTML文件中,然后使用templateUrl来指向他。

在定义指令的时候,除了以上两个最基础的属性外,我们还会使用到其他的很多属性,那么下面我们就来一一的说下:

1、priority[number]属性,这个属性是来规定自定义的指令的优先级的,当一个DOM元素上面有一个以上的指令的时候,就需要去比较指令的优先级了,优先级高的指令先执行。这个优先级就是用来在执行指令的compile函数前,先排序的,那么关于compile这个函数,我们会在下面仔细的说下。

2、terminal[boolean]属性,该参数用来定义是否停止当前元素上比本指令优先级低的指令,如果值为true,就是正常情况,按照优先级高低的顺序来执行,如果设置为false,就不会执行当前元素上比本指令优先级低的指令。

3、replace[boolean]属性,这个属性用来规定生成的HTML内容是否会替换掉定义此指令的HTML元素。当我们把该属性的值设为true的时候,打开控制台看看你会发现这个指令生成的元素会是这样的:

浅析AngularJS中的指令

当我们设置为false的时候会是这样的:

浅析AngularJS中的指令

4、link[function]属性,在上面的例子中,我们自定义的指令其实没有多大意义,这只是一个最简单的指令,有好多的属性我们都没有为他定义,所以没有多大用途。比如这个link函数,它包括三个参数:scope、element、attrs。这个link函数主要是用来添加对DOM元素的事件监听、监视模型属性变化、以及更新DOM的。它里面三个参数:

一:scope参数,在我们没有为指令定义scope属性的时候,那么他代表的就是父controller的scope。

二:element参数,就是指令的jQLite(jQuery的子集)包装DOM元素。如果你在引入AngularJS之前引入了jQuery,那么这个元素就是jQuery元素,而不是jQLite元素。由于这个元素已经被jQuery/jQLite包装了,所以我们就在进行DOM操作的时候就不需要再使用 $()来进行包装。

三:attrs参数,它包含了该指令所在元素的属性的标准化参数对象。

5、scope[boolean or object]属性,该属性是用来定义指令的scope的范围,默认情况下是false,也就是说该指令继承了父controller的scope,可以随意的使用父controller的scope里的属性,但是这样的话就会污染到父scope里的属性,这样是不可取的。所以我们可以让scope取以下两个值:true和{}。

当为true的时候,表示让Angular给指令创建一个继承于父scope的scope。

var myapp=angular.module('myapp',[])
.controller('myctrl',['$scope', function ($scope) {
$scope.color='red';
}])
.directive('hello', function () {
return{
restrict:'AECM',
replace:true,
template:'<button ng-click="sayhello()" style="background-color: {{color}}">click me</button>',
scope:true,
link: function (scope,elements,attrs) {
elements.bind('click', function () {
elements.css('background-color','blue');
})
}
}
})

这里我们为父scope定义了一个color的属性,并赋值为red,在hello指令的scope属性中,我们给了true,所以angular就为这个指令创建了一个继承于父scope的scope,然后在template属性中,我们用{{}}使用了从父scope中继承过来的color属性,所以按钮会是红色的。

浅析AngularJS中的指令

当为{}的时候,表示创建一个隔离的scope,不会继承父scope的属性。但是在有的时候我们也要需要访问父scope里的属性或者方法,那么我们应该怎么办呢。angular早就为我们想到了这一点,有以下的三个办法可以让我们记性上面的操作:

一:使用@实现单向绑定,如果我们只给scope的这个{}值的话,那么上面代码的按钮的背景色将会是灰色的。,而如果我们需要使用父scope的color属性的时候,我们可以这样写:

scope{
color:'@color'
}
<hello color="{{color}}"></hello>

这里有两点需要注意:1、scope里的属性color代表的是模板{{}}这个表达式里面的color,两者必须一致。2、scope里的属性color的值,也就是@后面的color,表示的是下面的HTML元素里的属性color,所以这两者也必须一致,当这里的属性名和模板里表达式{{}}里面使用的名称相同的话,就可以省略掉@后面的属性名了,然后写成下面的形式。

scope{
color:'@'
}

从指令中scope的值可以看出,指令模板中的表达式{{}}里的color的指向的是当前元素元素的color属性,这个color属性的值就是父scope的属性color的值。父scope把他的color属性值传递给了当前元素的color属性,然后color属性又把值传递给了模板中表达式里的color,这个过程是单向的。

二:使用=实现双向绑定

.directive('hello', function () {
return{
restrict:'AECM',
replace:true,
template:'<button style="background-color: {{color}}">click me</button>',
scope:{
color:'='
},
link: function (scope,elements,attrs) {
elements.bind('click', function () {
elements.css('background-color','blue');
scope.$apply(function () {
scope.color='pink';
})
})
}
}
})
<hello color="color"></hello>
<input type="text" ng-model="color"/>

这里我们给指令的scope中的color属性和父scope中的color属性进行了双向绑定,并且给指令的link函数里,添加了一个单击事件,点击按钮会让按钮的颜色发生变化,并且改变指令scope的color属性的值,再给HTML页面中加了一个input标签,输出或者输入父scope的color属性的值。这里有一个地方需要注意:当前元素的属性名的值不用再加上{{}}这个表达式了,因为这里父scope传递的是一个真实的scope数据模型,而不是简单的字符串,所以这样我们就可以传递简单的字符串、数组、甚至复杂的对象给指令的scope。现在让我们来看看点击这个按钮将会发生什么。

浅析AngularJS中的指令

这里我们能看到,按钮的颜色变成了粉色的,说明点击让指令的scope的color属性发生了变化,从而导致按钮的颜色发生了变化。但是这里不仅仅是按钮发生了变化,注意看,input表单里的值也变成了pink,这就说明父scope的color属性也发生了变化。 另外,再让我们来给input里面输入一个颜色,看看发生什么变化。

浅析AngularJS中的指令

可以看出当我们在表单里输入另外一种颜色的时候,按钮的颜色也发生了变化,这就说明指令的scope的color属性被改变了。综上我们可以发现使用'='实现的是双向绑定。

三:使用&调用父scope里的方法

var myapp=angular.module('myapp',[])
.controller('myctrl',['$scope', function ($scope) {
$scope.color='red';
$scope.sayhello= function () {
alert('hello');
};
}])
.directive('hello', function () {
return{
restrict:'AECM',
replace:true,
template:'<button ng-click="sayhello()" style="background-color: {{color}}">click me</button>',
scope:{
color:'=',
sayhello:'&'
},
link: function (scope,elements,attrs) {
elements.bind('click', function () {
elements.css('background-color','blue');
scope.$apply(function () {
scope.color='pink';
})
})
}
}
})
<hello color="color" sayhello="sayhello()"></hello>
<input type="text" ng-model="color"/>

这里我们也有两个地方需要注意:1、我们不仅需要在模板中使用ng-click指令,绑定上要调用的父scope中的方法,而且要在给当前元素添加一个属性,并且这个属性指向要调用的父scope的方法。2、指令scope的属性sayhello、当前元素的属性sayhello、模板绑定的事件方法名sayhello这三者要一致。那么这样我们就可以点击按钮,弹出一个对话框了。

浅析AngularJS中的指令

6、transclude[boolean]属性,这个属性用来让我们规定指令是否可以包含任意内容

.directive('hello', function () {
return{
restrict:'AECM',
replace:true,
transclude:true,
scope:{},
template:'<div ng-transclude></div>',
}
})
<hello>
hello
<span>{{color}}</span>
</hello>

当他的值为true的时候,这是页面上输出的值。当为false的时候,页面上将会是空白的。这里有一个地方需要注意,就是<span>{{color}}</span>,这里的color是父scope里的color。并不是指令里的scope的color属性。

7、compile[function]参数,该方法有两个参数element,attrs,第一个参数element指指令所在的元素,第二个attrs指元素上赋予的参数的标准化列表。这里我们也有个地方需要注意:compile 函数不能访问 scope,并且必须返回一个 link 函数。但是如果没有设置 compile 函数,你可以正常地配置 link 函数,(有了compile,就不能用link,link函数由compile返回)。

.directive('hello', function () {
return{
restrict:'AECM',
replace:true,
translude:true,
template:'<button ng-click="sayhello()" style="background-color: {{color}}">click me</button>',
scope:{
color:'=',
sayhello:'&'
},
compile: function (element,attrs) {
return function (scope,elements,attrs) {
elements.bind('click', function () {
elements.css('background-color','blue');
scope.$apply(function () {
scope.color='pink';
})
})
};
}
}
})

现在让我们来点击这个按钮

浅析AngularJS中的指令

我们发现,这里点击按钮之后发生的事情和前面用link属性的一样,这其实是没有多少差别的。

其实在大多数的情况下,我们只需要使用 link 函数。这是因为大部分的指令只需要考虑注册事件监听、监视模型、以及更新DOM等,这些都可以在 link 函数中完成。 但是对于像 ng-repeat 之类的指令,需要克隆和重复 DOM 元素多次,在 link 函数执行之前由 compile 函数来完成。那么为什么我们需要两个分开的函数来完成生成过程,为什么不能只使用一个?要回答好这个问题,我们需要理解指令在Angular中是如何被编译的!

8、指令是如何被编译的

当我们的angular应用引导启动的时候,angular将会使用$compile服务遍历DOM元素,在所有的指令都被识别之后,将会调用指令的compile方法,返回一个link函数,然后将这个link函数添加到稍后执行的 link 函数列表中,这个过程被称为编译阶段。像ng-repeat这样的指令,需要被重复克隆很多次,compile函数只在编译阶段被执行一次,并且复制这些模板,但是link 函数会针对每个被复制的实例被执行。所以分开处理,让我们在性能上有一定的提高(这句话有点不太理解,我是从别的地方copy过来的。原文在这里http://blog.jobbole.com/62249/)。

9、controller[string or function]和require[string or string[]]参数,当我们想要允许其他的指令和你的指令发生交互时,我们就需要使用 controller 函数。当另一个指令想要交互时,它需要声明它对你的指令 controller 实例的引用(require)。

.directive('hello', function () {
return{
scope:{},
require:'^he',
compile: function (element,attrs) {
return function (scope,elements,attrs,cntIns) {
cntIns.fn()
};
}
}
})
.directive('he', function () {
return {
restrict:'AE',
scope:{},
controller: function ($scope, $compile, $http) {
this.fn= function () {
alert('hello');
};
}
}
})
<he>
<hello color="color" sayhello="sayhello()"></hello>
</he>

当页面加载完毕之后,会弹出一个对话框。

浅析AngularJS中的指令

好了上面就是我这段时间学习angular,所了解到的指令的知识,就先写到这里了。

Javascript 相关文章推荐
jQuery 性能优化指南(3)
May 21 Javascript
解析javascript 数组以及json元素的添加删除
Jun 26 Javascript
Javascript访问器属性实例分析
Dec 30 Javascript
浅谈利用JavaScript进行的DDoS攻击原理与防御
Jun 04 Javascript
JQuery实现简单的图片滑动切换特效
Nov 22 Javascript
Angularjs 滚动加载更多数据
Mar 17 Javascript
JavaScript正则替换HTML标签功能示例
Mar 02 Javascript
Express框架之connect-flash详解
May 31 Javascript
微信小程序Redux绑定实例详解
Jun 07 Javascript
vue多页面开发和打包正确处理方法
Apr 20 Javascript
AngularJS实现的鼠标拖动画矩形框示例【可兼容IE8】
May 17 Javascript
详解React路由传参方法汇总记录
Nov 29 Javascript
简述Matlab中size()函数的用法
Mar 20 #Javascript
详解Matlab中 sort 函数用法
Mar 20 #Javascript
Sort()函数的多种用法
Mar 20 #Javascript
简单理解JavaScript中的封装与继承特性
Mar 19 #Javascript
JavaScript的函数式编程基础指南
Mar 19 #Javascript
深入解析JavaScript中函数的Currying柯里化
Mar 19 #Javascript
Linux下为Node.js程序配置MySQL或Oracle数据库的方法
Mar 19 #Javascript
You might like
全国FM电台频率大全 - 23 四川省
2020/03/11 无线电
PHP笔记之:基于面向对象设计的详解
2013/05/14 PHP
php.ini修改php上传文件大小限制的方法详解
2013/06/17 PHP
php查看当前Session的ID实例
2015/03/16 PHP
php利用事务处理转账问题
2015/04/22 PHP
JS查看对象功能代码
2008/04/25 Javascript
Javascript表达式中连续的 &amp;&amp; 和 || 之赋值区别
2010/10/17 Javascript
让你的CSS像Jquery一样做筛选的实现方法
2011/07/10 Javascript
解析Jquery中如何把一段html代码动态写入到DIV中(实例说明)
2013/07/09 Javascript
多个datatable共存造成多个表格的checkbox都被选中
2013/07/11 Javascript
jQuery实现鼠标滑过遮罩并高亮显示效果
2013/07/16 Javascript
javascript中不提供sleep功能如何实现这个功能
2014/05/27 Javascript
jQuery实现的个性化返回底部与返回顶部特效代码
2015/10/30 Javascript
Jquery promise实现一张一张加载图片
2015/11/13 Javascript
NodeJs中express框架的send()方法简介
2017/06/20 NodeJs
Angular 4中如何显示内容的CSS样式示例代码
2017/11/06 Javascript
JavaScript实现的开关灯泡点击切换特效示例
2019/07/08 Javascript
Python中模块与包有相同名字的处理方法
2017/05/05 Python
Python实现好友全头像的拼接实例(推荐)
2017/06/24 Python
Python计算一个给定时间点前一个月和后一个月第一天的方法
2018/05/29 Python
详解pandas安装若干异常及解决方案总结
2019/01/10 Python
Tornado实现多进程/多线程的HTTP服务详解
2019/07/25 Python
python3的pip路径在哪
2020/06/23 Python
一个不错的HTML5 Canvas多层点击事件监听实例
2014/04/29 HTML / CSS
家庭户外服装:Hawkshead
2017/11/02 全球购物
ALDO英国官网:加拿大女鞋品牌
2018/02/19 全球购物
加拿大在线隐形眼镜和眼镜店:VisionPros
2019/10/06 全球购物
自1926年以来就为冰岛保持温暖:66°North
2020/11/27 全球购物
物理系毕业生自荐信
2013/11/01 职场文书
实习生的自我评价
2014/01/08 职场文书
县优秀教师事迹材料
2014/01/31 职场文书
小学毕业典礼主持词
2014/03/27 职场文书
小学班主任评语大全
2014/04/23 职场文书
公安机关纪律作风整顿剖析
2014/10/10 职场文书
2019七夕节祝福语36句,快来收藏吧
2019/08/06 职场文书
ubuntu开机后ROS程序自启动问题
2022/12/24 Servers