AngularJS指令用法详解


Posted in Javascript onNovember 02, 2016

本文实例讲述了AngularJS指令用法。分享给大家供大家参考,具体如下:

指令(directives)是任何AngularJS应用中最重要的成分。尽管AngularJS已经自带了很多指令,你经常会发现需要自己亲手创建一些特别的指令。本文将会带你了解自定义指令并解释如何在现实世界中的Angular项目中使用它们。文章的最后,我们将一起用Angular指令来创建一个简单的笔记小应用。

综述

一个指令就是一个引入新语法的东西。指令是在DOM元素上做的标记,并同时附加了一些特定的行为。例如,静态的HTML并不知道如何来创建并显示一个日期选择插件。为了将这个新语法教给HTML我们需要一条指令。这个指令将会创建一个充当日期选择器的元素。我们将在随后看到如何实现这个指令。

如果你之前已经编写过Angular应用,那么你已经使用过指令了,不管你有没有意识到这点。你可能已经使用过像是ng-model,ng-repeat,ng-show等等这样的指令。所有这些指令都将特定的功能绑定到了DOM元素之上。例如,ng-repeat会重复特定的元素,而ng-show会有条件的展示元素。如果你想要创建一个可拖动元素的话你可能需要创建一个指令。指令背后的基本思想很简单。它通过在元素上绑定事件监听器并且将DOM变形来使HTML变得具有交互性。

从jQuery的角度来看指令

想想你如何使用jQuery来创建一个日期选择器。我们首先在HTML中添加一个普通的input字段然后在jQuery中我们调用$(element).dataPicker()来将其转换为一个日期选择器。但是,考虑一下。当一个设计师想要来检查这个标记时,他/她能够立刻猜出这个字段究竟是干什么用的吗?它仅仅是一个普通的input字段还是一个日期选择器?你必须要查看jQuery来确认这点。Angular的方法是使用指令来扩展HTML。因此,一个日期选择器的指令看上去可能如下所示:

<date-picker></date-picker>

或者如下所示:

<input type='text' data-picker/>

这种创建UI成分的方法既直观又清楚。你可以看到元素就知道它的用途。

创建自定义指令

一个Angular指令可能以四种形式出现:

1.一个新的HTML元素(<date-picker></date-picker>)

2.一个元素上的属性(<input type='text' date-picker/>)

3.作为一个类(<input type='text' class='date-picker'/>)

4.作为注释(<!--directive:date-picker-->)

当然,我们完全可以决定我们的指令以什么形式出现在HTML中。现在,我们来看看一个典型的Angular指令是如何写成的。它和controller的注册方式类似,但是它会返回一个简单的对象(指令定义),其中那个包含有一些配置指令的属性。下面的代码展示了一个简单和Hello World指令:

var app = angular.module('myapp',[]);
app.directive('helloWorld',function(){
  return {
    restrict: 'AE',
    replace: true,
    template: '<h3>Hello World!</h3>'
  }
});

在上面的代码中,app.diretive()函数在我们的模块中注册了一个新的指令。这个函数的第一个参数是指令的名称。第二个参数是一个返回指令定义对象的函数。如果你的指令对额外的对象/服务(services)例如 $rootScope, $http 或者 $compile 有依赖,它们也可以在其中被注入。这个指令可以作为一个HTML元素来使用,如下所示:

<hello-world/>

或者:

<hello:world/>

或者作为一个属性来使用:

<div hello-world></div>

或者:

<div hello:world/>

如果你想要兼容HTML5,你可以在属性前面加上x-或者data-前缀。因此,下面的标记将会匹配helloWorld指令:

<div data?hello?world></div>

或者

<di vx?hello?world></div>

注意

当匹配指令时,Angular会从元素/属性名之前去除前缀x-或者data-。然后将分隔符 - 或者 : 转换为驼峰表示法已匹配注册的指令。这就是为什么我们的helloWorld指令用在HTML中的时候实际上写成了hello-world。

尽管上面的这个简单的指令仅仅只是展示了一些静态的文本,其中还是有一些值得我们去探究的有趣的点。我们已经在这个指令定义对象中使用了三个属性。我们来看看这三个属性分别都有什么用:

restrict - 这个属性指明了一个指令应该如何在HTML中使用(记住指令可以以四种方式出现)。在这个例子中我们将它设置为'AE'。因此,这条指令可以作为一个HTML元素或者一个属性来使用。为了允许指令作为一个类来使用我们可以将restrict设置为'AEC'。

template - 这个实行指明了当指令被Angular编译和链接时生成的HTML标记。它不一定是一个简单的字符串。template可以很复杂,其中经常会涉及其它的指令,表达式({{}}),等等。在大多数情况下你可能会想要使用templateUrl而不是template。因此,理想情况下你应该首先将模板放置在一个单独的HTML文件中然后让templateUrl指向它。

replace - 这个属性指明了是否生成的模板会代替绑定指令的元素。在前面的例子中我们在HTML中使用指令为<hello-world></hello-world>,并将replace属性设置为true。因此,在指令编译后,生成的模板代替了<hello-world></hello-world>。最后的输出结果是<h3>Hello World!</h3>。如果你将replace设置为false,默认情况下,输出模板将会被插入到指令被调用的元素中。

link函数和作用域

有一个指令生成的模板是没有用的除非它在正确的作用域中北编译。默认情况下一个指令并不会得到一个新的子作用域。然而,它可以得到父作用域。这意味着如果一个指令位于在一个控制器中那么它将使用控制器的作用域。

为了利用作用域,我们可以使用一个叫做link的函数。它可以通过指令定义对象中的link属性来配置。我们现在对helloworld指令做一些修改一遍当用户在一个input字段中输入一个颜色名称时,Hello Wolld文字的背景颜色会自动发生改变。同样,当一个用户点击Hello World文字时,背景颜色会重置为白色。相应的HTML标记如下所示:

<body ng-controller='MainCtrl'>
  <input type='text' ng-model='color' placeholder='Enter a color' / >
  <hello-wolrd/>
</body>

修改后的helloWorld指令代码如下所示:

app.directive('helloWorld',function(){
  return {
    restrict: 'AE',
    replace: true,
    template: '<p style="background-color:{{color}}"></p>',
    link: function(scope,elem,attr){
      elem.bind('click',function(){
        elem.css('background-color','white');
      scope.$apply(function(){
        scope.color = "white";
      });
      });
      elem.bind('mouseover',function(){
        elem.css('cursor','pointer');
      });
    }
  }
});

注意到link函数被用在了指令中。它接收三个参数

scope - 它代表指令被使用的作用域。在上面的例子中它等同于符控制器的作用域。

elem - 它代表绑定指令的元素的jQlite(jQuery的一个自己)包裹元素。如果你在AngularJS被包含之前就包括了jQuery,那么它将变成jQuery包裹元素。由于该元素已经被jQuery/jQlite包裹,我们没有必要将它包含在$()中来进行DOM操作。

attars - 它代表绑定指令的元素上的属性。例如,如果你在HTML元素上有一些指令形式为:<hello-world some-attribute></hello-world>,你可以在link函数内用attrs.someAttribute来引用这些属性。

link函数主要是用来对DOM元素绑定事件监听器,监视模型属性变化,并更新DOM。在前面的指令代码中,我们绑定了两个监听器,click和mouseover。click处理函数重置了

的背景颜色,而mouseover处理函数则将游标改变为pointer。模板中拥有表达式{{color}},它将随着父作用域中的模型color的变化而变化,从而改变了Hello World的背景色。

Compile函数

Compile函数主要用来在link函数运行之前进行一些DOM转化。它接收下面几个参数:

tElement - 指令绑定的元素

attrs - 元素上声明的属性

这里要注意compile不能够访问scope,而且必须返回一个link函数。但是,如果没有compile函数以依然可以配置link函数。compile函数可以被写成下面的样子:

app.directive('test',function(){
  return {
    compile: function(tElem,attrs){
      //在这里原则性的做一些DOM转换
      return function(scope,elem,attrs){
       //这里编写link函数
      }
    }
  }
});

大多数时候,你仅仅只需要编写link函数。这是因为大部分指令都只关心与注册事件监听器,监视器,更新DOM等等,它们在link函数中即可完成。像是ng-repeat这样的指令,需要多次克隆并重复DOM元素,就需要在link函数运行之前使用compile函数。你可能会问威慑呢么要将两个函数分别使用。为什么我们不能只编写一个函数?为了回答这个问题我们需要理解Angular是如何编译指令的!

指令是如何被编译的

当应用在启动时,Angular开始使用$compile服务解析DOM。这项服务会在标记中寻找指令然后将它们各自匹配到注册的适龄。一旦所有的指令都已经被识别完成,Angular就开始执行它们的compile函数。正如前面所提到的,compile函数返回一个link函数,该函数会被添加到稍后执行的link函数队列中。这叫做编译阶段(compile phase)。注意到即使同一个指令有几个实例存在,compile函数也只会运行一次。

在编译阶段之后就到了链接阶段(link phase),这时link函数就一个接一个的执行。在这个阶段中模板被生成,指令被运用到正确的作用域,DOM元素上开始有了事件监听器。不像是compile函数,lin函数会对每个指令的实例都执行一次。

改变指令的作用域

默认情况下指令应该访问父作用域。但是我们并不像对所有情况一概而论。如果我们对指令暴露了父控制器的scope,那么指令就可以自由的修改scope属性。在一些情况下你的指令可能想要添加一些只有内部可以使用的属性和函数。如果我们都在父作用域中完成,可能会污染了父作用域。因此,我们有两种选择:

一个子作用域 - 这个作用域会原型继承父作用域。

一个隔离的作用域 - 一个全新的、不继承、独立存在的作用域。

作用域可以由指令定义对象中的scope属性定义。下面的例子展示了这一点:

app.directive('helloWorld',function(){
  return {
    scope: true, //使用一个继承父作用域的自作用域
    restrict: 'AE',
    replace: true,
    template: '<h3>Hello World!</h3>'
  }
});

上面的代码要求Angular为指令提供一个能够原型继承父作用域的子组用于。另一种情形,一个隔离作用域,代码如下所示:

app.directive('helloWorld',function(){
  return {
    scope: {}, //使用一个全新的隔离作用域
    restrict: 'AE',
    replace: true,
    template: '<h3>Hello World!</h3>'
  }
});

上面的指令使用一个不继承父作用域的全新隔离作用域。当你想要创建一个可重用的组件时隔离作用域是一个很好的选择。通过隔离作用域我们确保指令是自包含的兵可以轻松地插入到任何HTML app中。这种做法防止了父作用域被污染,由于它不可访问父作用域。在我们修改后的helloWorld指令中如果你将scope设置为{},那么代码就不会再正常运行。它将创建一个隔离的作用域然后表达式{{color}}将无法引用隔离作用域中的属性因此值变为undefined。

隔离作用域并不意味着你一点都不能获取到父作用域中的属性。有一些技巧可以使你访问父作用域中的属性同时监听这些属性的变化。我们将在后续文章中提到这种高级技巧。

希望本文所述对大家AngularJS程序设计有所帮助。

Javascript 相关文章推荐
this[] 指的是什么内容 讨论
Mar 24 Javascript
js url传值中文乱码之解决之道
Nov 20 Javascript
在jquery boxy中添加百度地图坐标拾取注意流程
Apr 03 Javascript
通过JS动态创建一个html DOM元素并显示
Oct 15 Javascript
JS 作用域与作用域链详解
Apr 07 Javascript
jQuery实现数字自动增加或者减少的动画效果示例
Dec 11 jQuery
javascript实现小型区块链功能
Apr 03 Javascript
JS回调函数简单易懂的入门实例分析
Sep 29 Javascript
vue在路由中验证token是否存在的简单实现
Nov 11 Javascript
keep-Alive搭配vue-router实现缓存页面效果的示例代码
Jun 24 Javascript
Vue登录拦截 登录后继续跳转指定页面的操作
Aug 04 Javascript
Canvas跟随鼠标炫彩小球的实现
Apr 11 Javascript
AngularJS表单和输入验证实例
Nov 02 #Javascript
AngularJS入门教程之数据绑定原理详解
Nov 02 #Javascript
深入理解Node.js 事件循环和回调函数
Nov 02 #Javascript
JavaScript 数组的深度复制解析
Nov 02 #Javascript
AngularJS实现与Java Web服务器交互操作示例【附demo源码下载】
Nov 02 #Javascript
Centos7 中 Node.js安装简单方法
Nov 02 #Javascript
AngularJS入门教程之与服务器(Ajax)交互操作示例【附完整demo源码下载】
Nov 02 #Javascript
You might like
PHP字符串长度计算 - strlen()函数使用介绍
2013/10/15 PHP
php将access数据库转换到mysql数据库的方法
2014/12/24 PHP
PHP自动生成表单代码分享
2015/06/19 PHP
js的一些常用方法小结
2011/06/29 Javascript
JavaScript操作XML 使用百度RSS作为新闻源示例
2012/02/17 Javascript
如何学习Javascript入门指导
2013/11/01 Javascript
JavaScript中的对象序列化介绍
2014/12/30 Javascript
jQuery实现仿腾讯迷你首页选项卡效果代码
2015/09/17 Javascript
谈谈我对JavaScript原型和闭包系列理解(随手笔记9)
2015/12/24 Javascript
利用CSS、JavaScript及Ajax实现图片预加载的方法
2016/11/29 Javascript
jQuery 插件实现随机自由弹跳气泡样式
2017/01/12 Javascript
js实现文字向上轮播功能
2017/01/13 Javascript
jQuery元素选择器实例代码
2017/02/06 Javascript
JS实现基于Sketch.js模拟成群游动的蝌蚪运动动画效果【附demo源码下载】
2017/08/18 Javascript
turn.js异步加载实现翻书效果
2019/07/25 Javascript
layui table设置某一行的字体颜色方法
2019/09/05 Javascript
[01:02:02]DOTA2上海特级锦标赛A组败者赛 EHOME VS CDEC第二局
2016/02/25 DOTA
Python httplib模块使用实例
2015/04/11 Python
python 调用HBase的简单实例
2016/12/18 Python
python实现字典(dict)和字符串(string)的相互转换方法
2017/03/01 Python
解决pycharm运行出错,代码正确结果不显示的问题
2018/11/30 Python
python 使用matplotlib 实现从文件中读取x,y坐标的可视化方法
2019/07/04 Python
python读取csv文件指定行的2种方法详解
2020/02/13 Python
Python基于jieba, wordcloud库生成中文词云
2020/05/13 Python
海量信息软件测试笔试题
2015/08/08 面试题
爱国演讲稿500字
2014/05/04 职场文书
经济信息系毕业生自荐信
2014/06/02 职场文书
房展策划方案
2014/06/07 职场文书
我心目中的好老师活动方案
2014/08/19 职场文书
幼儿园教师考核评语
2014/12/31 职场文书
高校自主招生教师推荐信
2015/03/23 职场文书
计划生育工作总结2015
2015/04/03 职场文书
医院党建工作总结2015
2015/05/26 职场文书
python3.7.2 tkinter entry框限定输入数字的操作
2021/05/22 Python
html5表单的required属性使用
2021/07/07 HTML / CSS
vue route新窗口跳转页面并且携带与接收参数
2022/04/10 Vue.js