angularJS之使用指令封装DOM操作

创建指令

指令也是一种服务,只是这种服务的定义有几个特殊要求:

  1. 必须使用模块的directive()方法注册服务
  2. 必须以对象工厂/factory()方法定义服务实现
  3. 对象工厂必须返回一个指令定义对象
  1. //定义指令的类工厂
  2. var directiveFactory = function(injectables){
  3. //指令定义对象
  4. var directiveDefinationObject = {
  5. ...
  6. };
  7. return directiveDefinationObject;
  8. };
  9. //在模块上注册指令
  10. angular.module("someModule",[])
  11. .directive("directiveName",directiveFactory);

INSIDE:指令在注入器中的登记名称是:指令名+Directive。 例如,ng-app指令的服务名称是:"ngAppDirective"。

示例(http://***/course/54f3ba65e564e50cfccbad4b/中“使用指令封装DOM操作”第一页)定义一个简单的指令ez-hoverable,这个指令被限制只能 出现在属性的位置,每个具有这个指令的HTML元素,将在鼠标移入 时以虚线边框突出显示。

指令定义对象

每个指令定义的工厂函数,需要返回一个指令定义对象。指令定义对象就是 一个具有约定属性的JavaScript对象,编译器/$compile在编译时就根据这 个定义对象对指令进行展开。

指令定义对象的常用属性如下:

  • template : string

使用template指定的HTML标记替换指令内容(或指令自身)

  • restrict : string

用来限定指令在HTML模板中出现的位置。

  • replace : true|false

使用这个属性指明template的替换方式。

  • scope : true|false|{...}

scope属性为指令创建私有的作用域,这在创建可复用的Widget时非常有用。

  • link : function(..){...}

link属性是一个函数,用来在指令中操作DOM树、实现数据绑定。

  • transclude : true|false|'element'

允许指令包含其他HTML元素,这通常用于实现一个容器类型的Widget。

template:定义替换模板

最简单的指令只需要使用template属性进行模板替换就可以实现。

template指明一个HTML片段,可以用来:

  • 替换指令的内容。这是默认的行为,可以使用replace属性更改。
  • 如果replace = true,那么用HTML片段替换指令本身。
  • 包裹指令的内容,如果transclue属性为true。

示例(http://***/course/54f3ba65e564e50cfccbad4b/中“使用指令封装DOM操作”第三页)实现了一个ezCustomer指令,这个指令只是简单的使用template指定的 模板替换ez-customer的内容:

restrict:限制指令的出现位置

restict属性可以是EACM这四个字母的任意组合,用来限定指令的应用场景。 如果不指定这个属性,默认情况下,指令将仅允许被用作元素名和属性名:

  • E - 指令可以作为HTML元素使用
  • A - 指令可以作为HTML属性使用
  • C - 指令可以作为CSS类使用
  • M - 指令可以在HTML注释中使用

我们对之前的示例,增加一个restrict属性,限制这个只能作为元素名使用。 代码已经预置到右边,你可以看到,现在唯一合法的方式是使用如下方式应用指令:

  1. <ez-customer></ez-customer>

考查编译后的DOM结构,你会发现ez-customer这个”伪“HTML标签还被保留着,这有时让完美 主义者有点闹心:

replace:模板的使用方式

我们希望使用template完整地替换原始的DOM对象,而不是填充其内容,replace 属性负责这件事。

replace属性指明使用template时,如何替换指令元素:

  • true - 编译时,将使用template替换指令元素
  • false - 编译时,将使用template替换指令元素的内容

示例(http://***/course/54f3ba65e564e50cfccbad4b/中“使用指令封装DOM操作”第五页)增加了replace属性,值为true意味着这个指令要求编译器使用template 替换原始的DOM元素:

你可能注意到模板的内容稍微修改了一下,这是因为replace为true时,要求模板有 一个根节点。

作用域问题

默认情况下,指令没有自己的scope对象,换句话说,它使用所在DOM对象对应的scope对象。

那么问题来了,如果一个指令在同一个scope内出现多次,会怎样?

  1. <div ng-controller="ezCtrl">
  2. <ez-customer></ez-customer>
  3. <ez-customer></ez-customer>
  4. </div>

没错,由于两个ez-customer指令都处在ezCtrl开辟的作用域内,所以两个指令绑定到了同样的 数据模型上,得到的是重复的结果。

显然,我们可以将每个ez-customer指令置于不同的作用域下,这意味着我们给每个ez-customer 一个不同的控制器:

  1. <div ng-controller="ezCtrl1">
  2. <ez-customer></ez-customer>
  3. </div>
  4. <div ng-controller="ezCtrl2">
  5. <ez-customer></ez-customer>
  6. </div>

看起来很怪异,对吗?

scope:使用隔离的作用域

通过设置scope属性,指令的每个实例都将获得一个隔离的本地作用域:

  1. var ezCustomerDirectiveFactory = function(){
  2. return {
  3. restrict:"E",
  4. replace:true,
  5. scope:{
  6. name : "@name",
  7. address : "=address"
  8. },
  9. template:"<div>name:{{name}} address:{{address}}</div>"
  10. }
  11. }

在上面的例子中,我们在本地scope上定义了两个属性:name和address,这样在 模板中就可以使用name和address了。

你应该已经注意到,name属性的值之前有一个@符号,这是一个约定好的标记,它 告诉编译器,本地scope上的name值需要从应用这个指令的DOM元素的name属性值 读取,如果DOM元素的name属性值变了,那么本地scope上的name值也会变化。

同样,address属性之前的=符号也是一个约定好的标记,它告诉编译器,本地scope 上的address属性值和DOM元素的address属性值指定的外部scope对象上的模型需要 建立双向连接:外部scope上模型的变化会改变本地scope上的address属性,本地 scope上address属性的变化也会改变外部scope上模型的变化。

有点绕,上个图:

从图中可以看出:

  1. 指令的template绑定的是本地scope上的name和address。
  2. 本地scope的name属性的值始终是ez-customer对象上name属性的值
  3. 本地scope的address属性值始终和ez-customer对应的scope对象上的Emmy.address 保持同步。

link:在指令中操作DOM

如果需要在指令中操作DOM,我们需要在对象中定义link属性,link函数的定义如下:

  1. function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }

注意link函数的参数,AngularJS在编译时负责传入正确的值:

  • scope

指令对应的scope对象。如果指令没有定义自己的本地作用域,那么传入的就是外部的 作用域对象。

  • iElement

指令所在DOM对象的jqLite封装。如果使用了template属性,那么iElement对应 变换后的DOM对象的jqLite封装。

  • iAttrs

指令所在DOM对象的属性集。这是一个Hash对象,每个键是驼峰规范化后 的属性名。

后两个参数我们先略过。

示例

示例(http://***/course/54f3ba65e564e50cfccbad4b/中“使用指令封装DOM操作”第八页)中,我们实现了一个可以指定显示格式的小时钟指令:ezCurrentTime。和原来一样, 我们在link函数中启动定时器,并在定时器中更新DOM。有几点解释下:

  1. 我们在scope上使用$watch()方法对format的值进行监听,并使用这个值调整显示格式
  2. 我们监听element的$destroy事件,这个事件是在DOM对象销毁时触发。我们在这个事件触发时 销毁定时器以释放资源
  3. 我们使用了AngularJS内置的$interval服务,而不是setInterval()函数创建定时器。
  4. 我们使用了AngularJS内置的dateFilter过滤器服务,对时间的显示进行格式化。 和$interval一样,dateFilter服务也是通过注入器注入的。

transclude:包含其他元素

有些指令需要能够包含其他未知的元素。比如我们定义一个指令ez-dialog,用来 封装对话框的样式和行为,它应当允许在使用期(也就是在界面模板文件里)才指 定其内容:

  1. <ez-dialog>
  2. <p>对话框的内容在我们开发ez-dialog指令的时候是无法预计的。这部分内容需要
  3. 被转移到展开的DOM树中适当的位置。</p>
  4. </ez-dialog>

transclude属性可以告诉编译器,利用所在DOM元素的内容,替换template中包含 ng-transclude指令的元素的内容:

从上图中可以看到,使用transclude有两个要点:

  1. 需要首先声明transclude属性值为true,这将告诉编译器,使用我们这个指令的 DOM元素,其内容需要被复制并插入到编译后的DOM树的某个点。
  2. 需要在template属性值中使用ng-transclude指明插入点。

右边嵌入了ez-dialog的实现实例(http://***/course/54f3ba65e564e50cfccbad4b/中“使用指令封装DOM操作”第九页)。

参考资料:http://***/

 
 
标签: angularjsangularnodejs

angularJS之使用指令封装DOM操作的更多相关文章

  1. 原生js封装dom操作库

    var utils = (function(window) { var flag = "getComputedStyle" in window; function win(attr ...

  2. React.js 小书 Lesson21 - ref 和 React.js 中的 DOM 操作

    作者:胡子大哈 原文链接:http://huziketang.com/books/react/lesson21 转载请注明出处,保留原文链接和作者信息. 在 React.js 当中你基本不需要和 DO ...

  3. ref 和 React.js 中的 DOM 操作

    在 React.js 当中你基本不需要和 DOM 直接打交道.React.js 提供了一系列的 on*方法帮助我们进行事件监听,所以 React.js 当中不需要直接调用 addEventListen ...

  4. 摆脱DOM操作,从TodoMVC看angularJS

    取代jQuery? 我很久之前便听说了angularJS的大名,之前的leader也经常感叹angularJS的设计如何如何精妙,可叹一直没有机会深入了解,国庆长假因为没钱出游,倒是可以对他做一个了解 ...

  5. [译]AngularJS中DOM操作

    再翻译一篇干货短文,原文:AngularJS jQuery 虽然Angularjs将我们从DOM的操作中解放出来了,但是很多时候我们还是会需要在controller/view加载之后执行一些DOM操作 ...

  6. 关于js封装框架类库之DOM操作模块(二)

    上一篇基本实现了框架结构,但是与真正能用上的项目框架比较还是存在很多不足,在这又做了些加强与优化 (function ( window, undefined ) { var arr = [], pus ...

  7. 关于js封装框架类库之DOM操作模块(一)

    在前端开发的过程中,javascript极为重要的一个功能就是对DOM对象的操作,而对其封装就是为了更好地进行DOM操作,提高浏览器的支持效率 现在给出一个案例:页面创建三个div,然后给其添加样式 ...

  8. DOM操作和样式操作库的封装

    一.DOM常用方法和属性复习 以下粗略的罗列一下DOM的常用方法和属性,由于不是介绍DOM的基础内容,所以就不一一详细说明各个方法和属性了(学习DOM的封装的,一般都对基础DOM比较熟悉了). 1.1 ...

  9. AngularJS指令封装高德地图组件

    1 概述 公司移动门户原来是基于AngularJS指令封装的百度地图组件,用于签到.签退.定位等功能,在使用过程中发现百度地图频繁的弹出广告,所以打算重新引用其它地图组件,最后决定基于AngularJ ...

随机推荐

  1. 安装framework 3.5出现0x800F0922的解决方法

    关闭Windows 防火墙,启动Windows model install服务 重启! 再安装 就ok了

  2. MVC5+EF6 入门完整教程 总目录

    本系列文章会从一个主干开始,逐渐深入,初步规划30篇.初级10篇,中级10篇,综合项目实战10篇 初级10篇 MVC5+EF6 入门完整教程10:多对多关联表更新&使用原生SQL@201505 ...

  3. POJ 2756 Autumn is a Genius 采用string大数减法

    标题意味着小神童.加减可以计算. 只是说这个小神童的学科知识,究竟有多神,自己给自己找. 最后,因为数据是非常非常巨大的,我听说关闭50k结束了50000数字总和,可以想见他神教. 这似乎也是考试题目 ...

  4. Html 5 坦克大战(韩顺平县版本号)

    html 5例如,下面的代码段: <!DOCTYPE html> <html> <head> <meta charset="utf-8"/ ...

  5. Java多播通讯框架 JGroups(转)

    JGroups是一个可靠的群组通讯Java工具包.它基于IP组播(IP multicast),但在可靠性,组成员管理上对它作了扩展. JGroups的可靠性体现在: 1,对所有接收者的消息的无丢失传输 ...

  6. 随着MapReduce job实现去加重,多种输出文件夹

    总结以往的工作中遇到的一个问题. 背景: 操作和维护与scribe从apacheserver一再被推到日志记录,所以在这里ETL处理正在进行的重.有根据业务的输出类型是用于多文件夹一个需求.方便挂分区 ...

  7. 手把手教你如何加入到github的开源世界! (转)

    我曾经一直想加入到开源项目中,但是因为没有人指导流程,网上看了很多,基本都是说了个大概,如果你也是一个初出茅庐的人,那么,我将以自己提交的一次开源代码为例,教会你步入开源的世界. 1,首先登陆到htt ...

  8. ios save image to album

    - (void)savePhotoToAlbum { ZoomScrollView *zoomScrollView = (ZoomScrollView*)[self.scrollView viewWi ...

  9. 【JAVA】【NIO】5、Java NIO Scatter / Gather

    标题手段Java NIO该分散体浓缩 Java NIO内置支持分散与收集.的概念主要用于信道分散聚集的读写. 读出的分散体的一个通道被读多个数据buffer在.因此.数据分散到多个buffer中. 对 ...

  10. HDU 4283 You are the one(间隔DP)

    标题效果: The TV shows such as You Are the One has been very popular. In order to meet the need of boys ...