AngularJS中的指令全面解析(必看)


Posted in Javascript onMay 20, 2016

说到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的这个{}值的话,那么上面代码的按钮的背景色将会是灰色的。AngularJS中的指令全面解析(必看),而如果我们需要使用父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的时候,AngularJS中的指令全面解析(必看)这是页面上输出的值。当为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过来的。

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,所了解到的指令的知识,就先写到这里了。

以上这篇AngularJS中的指令全面解析(必看)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js简单的弹出框有关闭按钮
May 05 Javascript
浅析jQuery 3.0中的Data
Jun 14 Javascript
全面了解JavaScript对象进阶
Jul 19 Javascript
codeMirror插件使用讲解
Jan 16 Javascript
three.js实现3D模型展示的示例代码
Dec 31 Javascript
详解tween.js 中文使用指南
Jan 05 Javascript
利用js给datalist或select动态添加option选项的方法
Jan 25 Javascript
vue-cli3.0配置及使用注意事项详解
Sep 05 Javascript
node实现socket链接与GPRS进行通信的方法
May 20 Javascript
JS对象属性的检测与获取操作实例分析
Mar 17 Javascript
有关vue 开发钉钉 H5 微应用 dd.ready() 不执行问题及快速解决方案
May 09 Javascript
Js和VUE实现跑马灯效果
May 25 Javascript
浅析angularJS中的ui-router和ng-grid模块
May 20 #Javascript
javascript的理解及经典案例分析
May 20 #Javascript
JS中对象与字符串的互相转换详解
May 20 #Javascript
JS 对象(Object)和字符串(String)互转方法
May 20 #Javascript
jQuery中通过ajax调用webservice传递数组参数的问题实例详解
May 20 #Javascript
浅析JavaScript Array和string的转换(推荐)
May 20 #Javascript
深入理解JS addLoadEvent函数
May 20 #Javascript
You might like
兼容PHP5的PHP目录管理函数库
2008/07/10 PHP
几个有用的php字符串过滤,转换函数代码
2012/05/01 PHP
php实现统计邮件大小的方法
2013/08/06 PHP
PHP中使用Imagick操作PSD文件实例
2015/01/26 PHP
php简单实现sql防注入的方法
2016/04/22 PHP
ThinkPHP下表单令牌错误与解决方法分析
2017/05/20 PHP
使用 laravel sms 构建短信验证码发送校验功能
2017/11/06 PHP
JS 字符串连接[性能比较]
2009/05/10 Javascript
jquery div 居中技巧应用介绍
2012/11/24 Javascript
jQuery学习笔记之jQuery动画效果
2013/09/09 Javascript
php读取sqlite数据库入门实例代码
2014/06/25 Javascript
jquery处理json对象
2014/11/03 Javascript
node.js中的console.time方法使用说明
2014/12/09 Javascript
JS判断两个对象内容是否相等的方法示例
2017/04/10 Javascript
js canvas实现适用于移动端的百分比仪表盘dashboard
2017/07/18 Javascript
利用node.js实现反向代理的方法详解
2017/07/24 Javascript
Vue框架中正确引入JS库的方法介绍
2017/07/30 Javascript
Vue中render方法的使用详解
2018/01/26 Javascript
基于jQuery实现Ajax验证用户名是否可用实例
2018/03/25 jQuery
聊聊Vue 中 title 的动态修改问题
2019/06/11 Javascript
a标签调用js的方法总结
2019/09/05 Javascript
layui 富文本赋值,取值,取纯文本值的实例
2019/09/18 Javascript
解决Vue 移动端点击出现300毫秒延迟的问题
2020/07/21 Javascript
微信小程序轮播图swiper代码详解
2020/12/01 Javascript
在Angular项目使用socket.io实现通信的方法
2021/01/05 Javascript
WebStorm无法正确识别Vue3组合式API的解决方案
2021/02/18 Vue.js
[02:08:58]2014 DOTA2国际邀请赛中国区预选赛 Ne VS CIS
2014/05/22 DOTA
Django REST Framework序列化外键获取外键的值方法
2019/07/26 Python
Python实现王者荣耀自动刷金币的完整步骤
2021/01/22 Python
HTML5图片预览实例分享
2014/06/04 HTML / CSS
女士时装鞋:Chinese Laundry
2018/08/29 全球购物
建筑施工员岗位职责
2013/11/26 职场文书
个人贷款收入证明
2014/10/26 职场文书
小学优秀教师材料
2014/12/15 职场文书
Nginx Rewrite使用场景及配置方法解析
2021/04/01 Servers
Python中npy和mat文件的保存与读取
2022/04/24 Python