本文整理并扩展了《AngularJS》这本书第六章里面的内容,此书近期即将由电子工业出版社出版,敬请期待口令:Angular

1.一点小说明

指令的作用:实现语义化标签

我们常用的HTML标签是这样的:

 <div>
<span>一点点内容</span>
</div>

而使用AngularJS的directive(指令)机制,我们可以实现这样的东西:

 <tabpanel>
<panel>子面板1</panel>
<panel>子面板2</panel>
</tabpanel>

很多人可能要惊呼,这货和JSP或者Struts等等框架里面的taglib很像啊!

呃,说实话,实际上就是这样的,只不过这里是使用JavaScript来实现的。正因为如此,所以很多taglib做不到的功能,使用它就都可以做到,比如访问N层scope里面的对象之类的事情(参见后面第5个例子)。

2.实例1:从最简单的开始

 <html ng-app='app'>
<body>
<hello></hello>
</body>
<script src="../angular-1.0.3/angular.min.js"></script>
<script src="HelloDirect.js"></script>
</html>

对于以上代码里面的<hello>标签,浏览器显然是不认识的,它唯一能做的事情就是无视这个标签。那么,为了让浏览器能够认识这个标签,我们需要使用Angular来定义一个hello指令(本质上说就是自己来把<hello>这种玩意儿替换成浏览器能识别的那些标准HTML标签)。

来看这段温馨的JS代码:

 var appModule = angular.module('app', []);
appModule.directive('hello', function() {
return {
restrict: 'E',
template: '<div>Hi there</div>',
replace: true
};
});

以上代码大概看两眼就可以了,不要太在意细节。

然后我们就可以在浏览器里面看到这样的内容:

实际产生的标签结构是这样的:

可以看到,<hello>这个东东已经被<div>Hi there</div>这个标签替换掉了,这也是以上JS代码里面replace:true这行配置的作用,代码里面的template配置 项当然就是我们要的div标签啦,至于restrict:'E'这个配置项的含义,请看下表:

ok,看完上面的表格,对于restrict这个属性相信你已经秒懂了,那么我们来玩儿点花样吧。如果我们需要替换的HTML标签很长,显然不能用 拼接字符串的方式来写,这时候我们可以用templateUrl来替代template,从而可以把模板写到一个独立的HTML文件中。

3.实例2:transclude(变换)

先看例子,JS代码:

 var appModule = angular.module('app', []);
appModule.directive('hello', function() {
return {
restrict: 'E',
template: '<div>Hi there <span ng-transclude></span></div>',
transclude: true
};
});

HTML代码:

 <html ng-app='app'>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
</head>
<body>
<hello>
<br/>
<span>原始的内容,</span><br/>
<span>还会在这里。</span>
</hello>
<hello>
</hello>
</body>
<script src="../angular-1.0.3/angular.min.js"></script>
<script src="Transclude.js"></script>
</html>

运行效果如下:

生成的HTML标签结构如下:

和第一个例子对比,这个例子的JS和HTML代码都略有不同,JS代码里面多了一个transclude: true,HTML代码里面在<hello>内部出现了子标签。

按照我们在第一个例子中的说法,指令的作用是把我们自定义的语义化标签替换成浏览器能够认识的HTML标签。那好,如果我们自定义的标签内部出现了子标签,应该如何去处理呢?很显然,transclude就是用来处理这种情况的。

对于当前这个例子,transclude的作用可以简化地理解成:把<hello>标签替换成我们所编写的HTML模板,但是<hello>标签内部的内容保持不变

很显然,由于我们没有加replace:true选项,所以<hello>标签还在,没有被替换掉。同时,通过这个例子你还还会发现一 个暗藏的属性,那就是浏览器实际上非常智能,虽然它并不认识<hello>这个标签,但是页面没有出错,它只是默默地把这个标签忽略掉了!怎 么样?是不是碉堡了?

你可以自己在上面的JS代码里面加上replace:true,然后再看生成的HTML结构。

4.实例3:关于compile和link

JS代码:

 var appModule = angular.module('app', []);
appModule.directive('hello', function() {
return {
restrict: 'E',
template: '<span>Hi there</span>',
replace: true
};
});
appModule.controller('MyController',function($scope) {
$scope.things = [1,2,3,4,5,6];
});

HTML代码:

 <html ng-app='app'>
<body ng-controller='MyController'>
<div ng-repeat='thing in things'>
{{thing}}.<hello></hello>
</div>
</body>
<script src="../angular-1.0.3/angular.min.js"></script>
<script src="CompileAndLink.js"></script>
</html>

呃,这个例子是用来解释一点点理论的,所以单纯看效果可能看不出个鸟。

如前所述,指令的本质其实是一个替换过程。好,既然如此,Angular到底是如何进行替换的呢?嗯嗯,这个过程分2个阶段,也就是本节标题所说的compile(编译)和link(连接)了。

简而言之,compile阶段进行标签解析和变换,link阶段进行数据绑定等操作。这里面更加细节的处理过程请参见《AngularJS》这本书中的解析,这里就不赘述了(呃,实际上是因为解释起来很长很麻烦,叔懒得在这儿说了

 )。

那么,知道这件事情有什么用途呢?

比方说,你有一些事件需要绑定到某个元素上,那么你需要提供一个link函数,做法请看下一个例子。

5.实例4:一个复杂一点的例子Expander

这是《AngularJS》这本书里面提供的一个例子,但是书里面没有给出完整的可运行代码,所以这里给出来,大家参考一下。

JS代码:

 var expanderModule=angular.module('expanderModule', [])
expanderModule.directive('expander', function() {
return {
restrict : 'EA',
replace : true,
transclude : true,
scope : {
title : '=expanderTitle'
},
template : '<div>'
+ '<div class="title" ng-click="toggle()">{{title}}</div>'
+ '<div class="body" ng-show="showMe" ng-transclude></div>'
+ '</div>',
link : function(scope, element, attrs) {
scope.showMe = false;
scope.toggle = function toggle() {
scope.showMe = !scope.showMe;
}
}
}
});
expanderModule.controller('SomeController',function($scope) {
$scope.title = '点击展开';
$scope.text = '这里是内部的内容。';
});

HTML代码:

 <html ng-app='expanderModule'>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<script src="../angular-1.0.3/angular.min.js"></script>
<link rel="stylesheet" type="text/css" href="ExpanderSimple.css"/>
</head>
<body>
<div ng-controller='SomeController'>
<expander class='expander' expander-title='title'>
{{text}}
</expander>
</div>
</body>
<script src="ExpanderSimple.js"></script>
</html>

CSS代码:

 .expander {
border: 1px solid black;
width: 250px;
}
.expander>.title {
background-color: black;
color: white;
padding: .1em .3em;
cursor: pointer;
} .expander>.body {
padding: .1em .3em;
}

运行效果如下:

注意一下JS代码里面的这一段:

 link : function(scope, element, attrs) {
scope.showMe = false;
scope.toggle = function toggle() {
scope.showMe = !scope.showMe;
}
}

自己跑一跑例子,研究一番,不多解释。

6.实例5:一个综合的例子

JS代码:

 var expModule=angular.module('expanderModule',[])
expModule.directive('accordion', function() {
return {
restrict : 'EA',
replace : true,
transclude : true,
template : '<div ng-transclude></div>',
controller : function() {
var expanders = [];
this.gotOpened = function(selectedExpander) {
angular.forEach(expanders, function(expander) {
if (selectedExpander != expander) {
expander.showMe = false;
}
});
}
this.addExpander = function(expander) {
expanders.push(expander);
}
}
}
});
expModule.directive('expander', function() {
return {
restrict : 'EA',
replace : true,
transclude : true,
require : '^?accordion',
scope : {
title : '=expanderTitle'
},
template : '<div>'
+ '<div class="title" ng-click="toggle()">{{title}}</div>'
+ '<div class="body" ng-show="showMe" ng-transclude></div>'
+ '</div>',
link : function(scope, element, attrs, accordionController) {
scope.showMe = false;
accordionController.addExpander(scope);
scope.toggle = function toggle() {
scope.showMe = !scope.showMe;
accordionController.gotOpened(scope);
}
}
}
});
expModule.controller("SomeController",function($scope) {
$scope.expanders = [{
title : 'Click me to expand',
text : 'Hi there folks, I am the content that was hidden but is now shown.'
}, {
title : 'Click this',
text : 'I am even better text than you have seen previously'
}, {
title : 'Test',
text : 'test'
}];
});

HTML代码:

 <html ng-app="expanderModule">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<script src="../angular-1.0.3/angular.min.js"></script>
<link rel="stylesheet" type="text/css" href="Accordion.css"/>
</head>
<body ng-controller='SomeController' >
<accordion>
<expander class='expander' ng-repeat='expander in expanders' expander-title='expander.title'>
{{expander.text}}
</expander>
</accordion>
</body>
<script src="Accordion.js"></script>
</html>

CSS代码:

 .expander {
border: 1px solid black;
width: 250px;
}
.expander>.title {
background-color: black;
color: white;
padding: .1em .3em;
cursor: pointer;
} .expander>.body {
padding: .1em .3em;
}

运行效果:

这个例子主要的难点在于如何在子Expander里面访问外层Accordion的scope中的数据,这一点解释起来略复杂,这里就不展开了,详细描述参见《AngularJS》一书 

AngularJS官方站点在这里:http://angularjs.org/

[全文完]

其它相关内容:

1、OReilly的《AngularJS》已由电子工业出版社出版

http://damoqiongqiu.iteye.com/blog/1965167

2、对比Angular/jQueryUI/Extjs:没有一个框架是万能的

http://damoqiongqiu.iteye.com/blog/1922004

3、AngularJS表单基础

http://damoqiongqiu.iteye.com/blog/1920191

4、AngularJS Form 进阶:远程校验和自定义输入项

http://damoqiongqiu.iteye.com/blog/1920993

5、AngularJS:在Windows上安装Yeoman

http://damoqiongqiu.iteye.com/blog/1885371

6、使用JsTestDriver实现JavaScript单元测试

http://damoqiongqiu.iteye.com/blog/1924415

《AngularJS》5个实例详解Directive(指令)机制的更多相关文章

  1. 转载 《AngularJS》5个实例详解Directive(指令)机制

    <AngularJS>5个实例详解Directive(指令)机制 大漠穷秋 本文整理并扩展了<AngularJS>这本书第六章里面的内容,此书近期即将由电子工业出版社出版,敬请 ...

  2. Vue 实例详解与生命周期

    Vue 实例详解与生命周期 Vue 的实例是 Vue 框架的入口,其实也就是前端的 ViewModel,它包含了页面中的业务逻辑处理.数据模型等,当然它也有自己的一系列的生命周期的事件钩子,辅助我们进 ...

  3. 我的书籍《深入解析Java编译器:源码剖析与实例详解》就要出版了

    一个十足的技术迷,2013年毕业,做过ERP.游戏.计算广告,在大公司呆过,但终究不满足仅对技术的应用,在2018年末离开了公司,全职写了一本书<深入解析Java编译器:源码剖析与实例详解> ...

  4. 实例详解 Java 死锁与破解死锁

    锁和被保护资源之间的关系 我们把一段需要互斥执行的代码称为临界区.线程在进入临界区之前,首先尝试加锁 lock(),如果成功,则进入临界区,此时我们称这个线程持有锁:否则呢就等待,直到持有锁的线程解锁 ...

  5. linux基础-磁盘阵列(RAID)实例详解

    磁盘阵列(RAID)实例详解 raid技术分类 软raid技术 硬raid技术 Raid和lvm的区别 为什么选择用raid RAID详解 RAID-0 RAID-1 RAID-5 Raid-10 R ...

  6. Cocos2d-x 3.X手游开发实例详解

    Cocos2d-x 3.X手游开发实例详解(最新最简Cocos2d-x手机游戏开发学习方法,以热门游戏2048.卡牌为例,完整再现手游的开发过程,实例丰富,代码完备,Cocos2d-x作者之一林顺和泰 ...

  7. JavaScript学习笔记-实例详解-类(二)

    实例详解-类(二)   //===给Object.prototype添加只读\不可枚举\不可配置的属性objectId(function(){ Object.defineProperty(Object ...

  8. JavaScript学习笔记-实例详解-类(一)

    实例详解-类(一): //每个javascript函数(除了bind())都自动拥有一个prototype对象// 在未添加属性或重写prototype对象之前,它只包含唯一一个不可枚举属性const ...

  9. Entity Framework实例详解

    Entity Framework Code First的默认行为是使用一系列约定将POCO类映射到表.然而,有时候,不能也不想遵循这些约定,那就需要重写它们.重写默认约定有两种方式:Data Anno ...

随机推荐

  1. 分布式架构 Hadoop 2.7.X 安装和配置

    一.安装环境 硬件:虚拟机 操作系统:Ubuntu 14 32位 IP:59.77.132.28主机名:admin安装用户:root 二.安装JDK 安装JDK1.7或者以上版本.这里安装jdk1.7 ...

  2. 11.6---矩阵查找元素(CC150)

    思路,一旦提到查找就要想到二分查找. public static int[] findElement(int[][] a, int n, int m, int key) { // write code ...

  3. 一步步教你Hadoop多节点集群安装配置

    1.集群部署介绍 1.1 Hadoop简介 Hadoop是Apache软件基金会旗下的一个开源分布式计算平台.以Hadoop分布式文件系统HDFS(Hadoop Distributed Filesys ...

  4. MySQL ODBC for Linux

    参考自http://blog.csdn.net/allens_zhou/article/details/8575400 centos7 64bit [IP:192.168.0.100] yum ins ...

  5. struts2 结果页面配置

    <result>标签: * 属性: * name:逻辑视图的名称 * type:结果页面类型. * dispatcher         :转发.默认值. * redirect       ...

  6. Java GUI学习笔记之初识AWT和Swing

    Frame f = new Frame(); //获取显示器的尺寸 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize() ...

  7. FastReport调用Delphi中的人民币大写转换自定义函数

    FastReport调用Delphi中的人民币大写转换自定义函数   FastReport调用Delphi中的人民币大写转换自定义函数 function TJzpzEdit1.MoneyCn(mmje ...

  8. 14. javacript高级程序设计-表单

    1. 表单脚本 1.1 基础知识 <from>元素表示表单: l acceptCharset:服务器能处理的字符集 l action:接受请求的URL l elements:表单中所有控件 ...

  9. Java for LeetCode 215 Kth Largest Element in an Array

    Find the kth largest element in an unsorted array. Note that it is the kth largest element in the so ...

  10. json格式

    $.post('text.action',{....},function(datas){ var name=datas.data[0].name; }); 如果是多个还可以用循环获取.$.post(' ...