图灵社区的电子书没有采用专有客 户端,您可以在任意设备上,用自 己喜欢的浏览器和PDF阅读器进行 阅读。
但您购买的电子书仅供您个人使 用,未经授权,不得进行传播。
我们愿意相信读者具有这样的良知 和觉悟,与我们共同保护知识产 权。
如果购买者有侵权行为,我们可能 对该用户实施包括但不限于关闭该 帐号等维权措施,并可能追究法律 责任。
数字版权声明
本书是学习AngularJS 的公认经典,内容全面,讲解通俗,适合各层次的学习者。作者拥有丰富的 AngularJS 开发和教学经验,也是一位全栈工程师。全书 35 章,由浅入深地讲解了 AngularJS 的基本概念和 基本功能,包括模块、作用域、控制器、表达式、指令、路由、依赖注入等,重要的是书中对每一个概念 的讲解都配合了恰如其分的示例和代码,让读者通过动手实践,切身体会到这些概念的含义和价值。本书 后半部分深入到AngularJS 应用开发,系统地讨论了服务器通信、事件、架构、动画、本地化、安全、缓存、
移动应用等主题。
本书适合各个层次的AngularJS 开发人员学习,无论是出于工作需要,还是好奇心的驱使,只要你想彻 底理解AngularJS,本书都会让你满载而归。
定价:99.00元
读者服务热线:(010)51095186转600 印装质量热线:(010)81055316 反盗版热线:(010)81055315
广告经营许可证:京崇工商广字第 0021 号 著 [美] Ari Lerner
译 赵望野 徐 飞 何鹏飞 责任编辑 李松峰
执行编辑 李 静 许林玉 责任印制 焦志炜
人民邮电出版社出版发行 北京市丰台区成寿寺路11号 邮编 100164 电子邮件 [email protected]
网址 http://www.ptpress.com.cn 北京 印刷 开本:800×1000 1/16 印张:29.75
字数:760千字 2014年 8 月第 1 版 印数:1 — 4 000册 2014年 8 月北京第 1 次印刷
著作权合同登记号 图字:01-2014-5140号
◆
◆
◆
版 权 声 明
Original edition, entitled The Complete Book on AngularJS Machines. Copyright © 2013 by Ari Lerner.
Simplified Chinese translation copyright © 2014 by Posts & Telecom Press.
All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage and retrieval system, without permission in writing from W. W. Norton & Company, Inc.
本书简体中文版由 Ari Lerner.授权人民邮电出版社独家出版。未经出版者许可,不得以任何 方式复制本书内容。
仅限于中华人民共和国境内(中国香港、澳门特别行政区和台湾地区除外)销售发行。
版权所有,侵权必究。
在微博上分享这本书
请帮Ari Lerner在新浪微博(http://weibo.com/)上宣传这本书。
推荐本书的微博:
#ngbook#我刚买了《AngularJS权威教程》!我准备构建高级、现代的Webapp!@图灵教育 点击下面这个链接,在新浪微博上搜索其他人对本书的评价:
https://huati.weibo.com/k/ngbook
1
2
3
5
7
10
12 8
9 4
6
11
献 词
我把这本书献给我的父母,Lisa Lerner和Nelson Lerner,因为没有他们的支持和鼓励就不可 能有这本书。
特别感谢
感谢可爱的Q,感谢你一直以来的激励,以及你在编辑方面的过人天赋。感谢我的共同创始
人兼朋友Nate Murray。
1
2
3
5
7
10
12 8
9 4
6
11
译 者 序
2012年上半年,我所在的公司正在开发一个二次开发平台,它的目标是从数据库开始,能自 由、方便地定制业务数据、规则、流程、服务接口,还有展现层。在对展现层的实现部分,我思 考了很久,对其中部分技术细节还是缺乏好的思路,于是把眼光转到开源社区,无意中发现了 AngularJS这样一个框架,详细考察之后,我认为它在很大程度上满足了我们的需求,继而投入 了不小的精力进行研究。
在这两年里,我差不多遍历了它的源码,了解了很多细节的实现机制,并且与当时研究得较 深的几位朋友,比如angularjs.cn的作者严清,资深开发者王宇鹏等进行了交流,获得了很多有益 的信息,与此同时,也跟Avalon的作者司徒正美有过一些讨论,对前端MV*有了更深入的认识。
后来,团队中的大漠穷秋翻译的《用AngularJS开发下一代Web应用》由电子工业出版社出版。作 为国内第一本关于AngularJS的译著,它带动了学习和了解AngularJS框架的浪潮,也因此与朴灵 的《深入浅出Node.js》一起,成为前端开发人员拓展思维和技能的两本最受欢迎图书。
到了2014年,我离开工作9年的地方,来到新的工作环境——苏宁云商,本来心里权衡过,
很可能不再有使用AngularJS的业务场景了,不曾想到入职之后面对的几个项目都属于云产品,
正适合使用这类框架,因此又继续了对它的深入研究。
在此期间,图灵公司的李松峰老师发布了本书招募译者的消息,我心里一动就联系了他。经 过沟通之后,我与另外两名译者,豌豆荚的@赵望野和腾讯的@basecss,合作翻译本书,每人负 责1/3的内容。第一次正式翻译图书,我很忐忑,翻译过程中也遇到了一些困难。此前我虽翻译 过一些技术文章,其中一篇恰好与AngularJS有关,但翻译图书跟翻译文章的差异很大,有很多 东西要考虑一致性和连贯性。
本书内容丰富,从零开始向读者讲述AngularJS,首先介绍AngularJS的基本概念,以及在一 些场景下的简单应用。接着,本书花很大篇幅讲解AngularJS的周边体系。我们使用这样一个框 架,自然需要对前端的架构有一些考虑,包括代码的组织,一些第三方库的选择,甚至还有项目 的建立、开发、测试、发布等各环节的综合考虑,这不再是一个简单的编码过程,而是一整套工 程化的流程。
另外,我们也可能需要为这样一套前端的技术栈选择相应的后端服务,比如,可以使用Node.js 自己建立,或者是利用互联网上已有的一些强大平台(比如Amazon等),在这些平台的帮助下,
我们的AngularJS应用将如虎添翼,到达新的高度。
使用一个框架却不去深入了解它的原理,就会一直流于表面,当面对比较复杂的场景时,就
找不到优化方案。因此,本书的后面部分也深入剖析了AngularJS的一些原理和拓展主题,比如
国际化、移动开发、调试、性能优化等。
无论是零基础的入门级开发者,还是有过一定经验的中高级开发人员,都能从本书中受益。
目前,前端MV*框架百花齐放,AngularJS只是其中较流行的一种。这些框架孰优孰劣,其 实并无定论,每个框架都会有它的适用场景,都有它优秀的一面,也没有哪个框架能够通吃所有 业务场景,如果因为对一个框架的喜爱,而把它引入到不适合的产品中,一定是有害无益。
因此,我们希望读者在阅读本书时,能够多思考,愿大家在学习本书过程中都能收获满满。
这样的话,作为本书译者的我们也将感同身受,与大家一同分享其中的喜悦和满足感。
在本书的出版过程中,除了我们三名译者之外,图灵公司的编辑李静也付出了很大的努力,
支付宝的玉伯、51JS版主宝玉、百度的berg提出了不少宝贵意见,对此,一并表示衷心感谢。
徐飞 2014年7月
何鹏飞的个人致谢
感谢我所在的团队,给我提供非常好的环境,让我能学习成长。
感谢我在腾讯的导师@TooBug,让我接触到很多新东西,当然包括这本书所讲述的 AngularJS。在翻译的过程中他也为我提供了很多帮助。
感谢图灵公司的出版团队,本书的出版离不开他们的努力和帮助。
最后,还要感谢没有在这里一一列出的帮助过我的每一个人。
赵望野的个人致谢
Web技术日新月异,每天早上翻看各种技术博客,都有一种逆水行舟不进则退的危机感,而 这两年来前端MV*框架无疑是Web前端开发领域最热门的话题之一。之前已经读过本书英文版,
其中涵盖了AngularJS开发的全部细节,示例代码清晰易懂,因此接下了中文版的翻译工作,并
迫不及待地推荐给所有想学习AngularJS的朋友。感谢李松峰老师的帮助,感谢图灵出版团队的
辛勤工作,感谢在翻译本书过程中给予帮助的所有朋友。
1
2
3
5
7
10
12 8
9 4
6
11
引 言
序
似乎每天都有新的JavaScript库或框架发布,对此我多少已经有些麻木了。有能力从众多的库 或框架中进行筛选是件好事,但至少在我看来,一个应用程序中如果包含了太多的脚本,对于维 护来说却是件坏事。随着应用程序中脚本数量的增加,脚本间会产生依赖关系,所以我一直期待 能有那么一到两个脚本,就提供我需要的所有核心功能。
当我第一次听说AngularJS时,它就立刻引起了我的注意,因为它只通过一个独立的框架就 可以构建动态、交互密集型的客户端应用。通过进一步的研究,我确信这个第一判断是正确的,
于是开始迷上了这个框架。AngularJS提供了一系列健壮的功能,以及将代码隔离成模块的方法,
这对提高可复用性、可维护性和可测试性都是非常有益的。它的核心功能包括DOM操作、动画、
模板、双向数据绑定、路由、历史管理、Ajax和测试,等等。
基于一个核心框架进行开发虽然很方便,但是学习它却充满挑战。一开始学习AngularJS时,
我迷失在各种不同的主题中,并很快变得有些沮丧,甚至开始怀疑它到底是不是我想要的。服务 是什么?它和工厂相比有什么区别?作用域服务是怎么同整个系统融合在一起的?指令是什么,
我为什么要使用它?将这些零碎的知识点拼在一起形成大局观是我最初要克服的障碍。如果能有 一些简明的参考资料,对于降低学习难度大有裨益。
很幸运,你已经有了这样一本优秀的参考资料,就是你手上的这本《AngularJS权威教程》,
它将帮助你提升学习效率。本书作者将他掌握的AngularJS知识倾囊相授,并以非常容易理解和 学习的方式呈现给大家。如果你想更深入地了解数据绑定、实时模板的工作原理、测试AngularJS 应用的流程、服务和工厂的作用以及作用域和控制器如何协同工作等知识,那么这本书就是你所 需要的。使用功能强大的AngularJS进行开发是一件非常有趣的事情,本书的示例将帮助你快速 掌握这个框架。祝你的AngularJS项目一切顺利!
Dan Wahlin,瓦林咨询公司
①致谢
首先,我要感谢一直鼓励我完成这本书的每一个人。那些说写书很容易的人,一定没有亲自 写过。
——————————
① 个人博客和Twitter页面网址为http://weblogs.asp.net/dwahlin和http://twitter.com/DanWahlin。
我还要亲自感谢Q Kuhns对本书语法方面不厌其烦的修改和支持,感谢Erik Trom耐心地对细 节进行修订,以及Nate Murray的清晰思路和乐观精神。
非常感谢Hack Reactor
①的全体成员在2013年的暑期课程中给了我一个探索如何在正式场合 讲授AngularJS的机会。
同时也要感谢我在30x500的校友们,Sean Iams、Michael Fairchild、Bradly Green、Misko Hevery 和整个AirPair团队。
最后,感谢那些对这本书的预览版提供帮助的人。我们从社区获得了非常棒的帮助和支持。
特别要感谢以下三位:
Philip Westwell
Saurabh Agrawal
Dougal MacPherson
关于本书
本书包含了能让你成为AngularJS
②高手的解决方案。AngularJS是由Google
③开发的先进前端 框架,借助它你可以快捷高效地开发富交互应用。
本书提供了一系列前沿工具,使你在很短的时间内就可以上手创建令人印象深刻的Web体 验。它能帮助你解决棘手的问题,并提供了一些可以立刻投入使用的实用技术。
本书涵盖的主题可以帮助你构建专业的Web应用,并能够非常顺利地执行。这些主题包括:
与RESTful风格的Web服务交互;
创建可复用的自定义组件;
测试;
异步编程;
创建服务;
提供先进的视觉效果;
其他更多内容。
本书的目标不仅是让你深刻了解AngularJS的运行原理,而且同时也提供了专业的代码片段,
你可以对它们进行修改,从而构建你自己的应用。
借助这些工具和测试,你可以着手使用AngularJS开发自己的动态Web应用了,并且确信你的 应用是可扩展的。
本书读者对象
本书写给那些从未使用AngularJS开发过Web应用,并且对如何开始使用这个优秀的框架心存 好奇的读者。我们假定读者已经掌握了HTML和CSS,并且熟悉JavaScript(或者其他JavaScript
——————————
① http://www.hackreactor.com
② http://angularjs.org
③ http://google.com
1
2
3
5
7
10
12 8
9 4
6
11
框架)的基础知识。
本书组织结构
首先,本书涵盖了入门的基础知识,目的是帮助你很快上手使用AngularJS开发动态Web应用。
接下来会介绍AngularJS的工作原理,以及它与其他流行的JavaScript框架的差异。我们会深 入讨论AngularJS应用内部的工作流程。
最后,我们将应用所学的知识开发一个相对复杂的应用程序。
其他资源
我们会引用AngularJS
①官方网站的文档。官方文档是非常好的学习资源,我们会经常用到它。
建议你先看一下AngularJS的API文档,通过它,你可以直接获得开发AngularJS应用的推荐方 法。同时,这个文档肯定也是最新的。
本书排版约定
本书使用如下排版规范来表示不同类型的信息。
单行代码是这样的: <h1>Hello</h1>
代码块如下所示:
var App = angular.module('App', []);
function FirstController($scope) { $scope.data = "Hello";
}
命令行中的命令如下所示:
$ ls -la
Chrome(开发过程中使用的主要浏览器)开发者控制台中的命令如下所示:
> var obj = {message: "hello"};
新术语使用楷体。
重点文字将会加粗。
提示和技巧用如下图标标示:
这个图标表示提示。
提醒和陷阱用警告图标标示:
这个图标表示警告。
——————————
① http://angularjs.org
错误信息用如下图标标示:
这个图标表示错误。
重要的补充内容使用如下图标标示:
信息框。
需要讨论的主题用如下图标标示:
这是一个讨论框。
开发环境
为了开发AngularJS应用,首先需要一个顺手的开发环境。在整个学习过程中,我们会将精 力主要放在两个环境中:编辑器和浏览器。
本书提到编辑器时指的是你使用的文本编辑器,而浏览器就是你使用的浏览器。强烈建议你 下载Google的Chrome浏览器,因为它提供了一个非常强大的开发环境,可以使用开发者工具。
开始之前,我们还需要安装一些库。为了运行测试,我们需要Karma和Node.js。最好也装上 git,但不强求。
本书不会介绍如何安装NodeJS。可以访问nodejs.org
①来获得更多信息
②。
虽然我们大部分工作都是在浏览器中完成的,但本书的部分内容也会重点介绍如何在服务器 端通过构建RESTful风格的API来服务前端。
——————————
① http://nodejs.org
② 读者还可以参考图灵公司的《深入浅出Node.js》一书。——编者注
目 录
第 1 章 初识 AngularJS
...11.1 浏览器如何获取网页 ...1
1.2 浏览器是什么 ...2
1.3 AngularJS 是什么 ...2
1.3.1 AngularJS 有什么不同...3
1.3.2 许可...3
第 2 章 数据绑定和第一个 AngularJS Web 应用
...42.1 AngularJS 中的数据绑定 ...5
2.2 简单的数据绑定 ...6
2.3 数据绑定的最佳实践 ...8
第 3 章 模块
...103.1 参数...11
3.1.1 name(字符串) ...11
3.1.2 requires(字符串数组)...11
第 4 章 作用域
...124.1 视图和$scope的世界 ...12
4.2 就是 HTML 而已...13
4.3 作用域能做什么 ...14
4.4 $scope的生命周期 ...14
4.4.1 创建...15
4.4.2 链接...15
4.4.3 更新...15
4.4.4 销毁...15
4.5 指令和作用域 ...15
第 5 章 控制器
...165.1 控制器嵌套(作用域包含作用域) ...18
第 6 章 表达式
...206.1 解析 AngularJS 表达式 ...20
6.2 插值字符串...21
第 7 章 过滤器
...247.1 自定义过滤器 ...29
7.2 表单验证... 29
第 8 章 指令简介
... 438.1 指令:自定义 HTML 元素和属性 ... 44
8.2 向指令中传递数据 ... 50
第 9 章 内置指令
... 569.1 基础ng属性指令 ...56
9.1.1 布尔属性... 56
9.1.2 类布尔属性 ... 58
9.2 在指令中使用子作用域 ...59
第 10 章 指令详解
... 7210.1 指令定义... 72
10.1.1 restrict(字符串) ...74
10.1.2 优先级(数值型) ... 75
10.1.3 terminal(布尔型) ...75
10.1.4 template(字符串或函数)...76
10.1.5 templateUrl(字符串或 函数)...76
10.1.6 replace(布尔型) ...77
10.2 指令作用域... 77
10.2.1 scope参数(布尔型或对象) ....78
10.2.2 隔离作用域 ... 80
10.3 绑定策略... 81
10.3.1 transclude... 82
10.3.2 controller(字符串或函数)....84
10.3.3 controllerAs(字符串)...86
10.3.4 require(字符串或数组)...86
10.4 AngularJS 的生命周期 ...87
10.4.1 编译阶段 ... 87
10.4.2 compile(对象或函数)...88
10.4.3 链接... 89
10.5 ngModel... 90
10.5.1 自定义渲染 ... 92
10.5.2 属性... 92
10.6 自定义验证... 93
第 11 章 AngularJS 模块加载
... 9511.1 配置 ... 95
11.2 运行块 ... 96
第 12 章 多重视图和路由
... 9812.1 安装 ... 98
12.2 布局模板 ... 99
12.3 路由 ... 99
12.4 $location服务...103
12.5 路由模式 ... 105
12.5.1 HTML5 模式 ... 105
12.5.2 路由事件... 106
12.5.3 关于搜索引擎索引... 107
12.6 更多关于路由的内容... 107
12.6.1 页面重新加载... 107
12.6.2 异步的地址变化... 107
第 13 章 依赖注入
... 10813.1 推断式注入声明... 109
13.2 显式注入声明 ... 110
13.3 行内注入声明 ... 110
13.4 $injector API ... 111
13.4.1 annotate()... 111
13.4.2 get()... 111
13.4.3 has()... 111
13.4.4 instantiate()... 112
13.4.5 invoke()... 112
13.5 ngMin... 112
13.5.1 安装 ... 113
13.5.2 使用ngMin... 113
13.5.3 工作原理... 113
第 14 章 服务
... 11414.1 注册一个服务 ... 114
14.2 使用服务 ... 116
14.3 创建服务时的设置项... 118
14.3.1 factory()... 119
14.3.2 service()... 119
14.3.3 provider()... 120
14.3.4 constant()... 122
14.3.5 value()... 122
14.3.6 何时使用value()和 constant()... 123
14.3.7 decorator()... 123
第 15 章 同外界通信:XHR 和服务器 通信
... 12515.1 使用$http... 125
15.2 设置对象 ... 128
15.3 响应对象 ... 130
15.4 缓存 HTTP 请求... 131
15.5 拦截器 ... 132
15.6 设置$httpProvider... 133
15.7 使用$resource... 134
15.8 安装 ... 134
15.9 应用$resource... 135
15.9.1 基于 HTTP GET 方法 ... 135
15.9.2 基于非 HTTP GET 类型的 方法 ...136
15.9.3 $resource实例...137
15.9.4 $resource实例是异步的 ...138
15.9.5 附加属性 ... 138
15.10 自定义$resource方法...138
15.11 $resource设置对象...139
15.12 $resource服务...141
15.13 使用 Restangular ... 142
15.14 Restangular 简介... 142
15.15 安装 Restangular ... 143
15.16 Restangular 对象简介... 144
15.17 使用 Restangular ... 145
15.17.1 我的 HTTP 方法们怎么办 .... 146
15.17.2 自定义查询参数和头... 147
15.18 设置 Restangular ... 147
第 16 章 XHR 实践
... 15316.1 跨域和同源策略 ... 153
16.2 JSONP ... 153
16.3 使用 CORS... 154
16.3.1 设置 ... 154
16.3.2 服务器端 CORS 支持 ... 155
16.3.3 简单请求 ... 155
16.3.4 非简单请求... 156
16.4 服务器端代理 ... 157
16.5 使用 JSON... 157
16.6 使用 XML ... 158
16.7 使用 AngularJS 进行身份验证 ... 159
16.7.1 服务器端需求... 159
16.7.2 客户端身份验证... 160
16.8 和 MongoDB 通信... 165
第 17 章 promise
...16817.1 什么是 promise ...168
17.2 为什么使用 promise ...169
17.3 Angular 中的 promise ...170
17.4 链式请求...173
17.4.1 all(promises)...174
17.4.2 defer()...174
17.4.3 reject(reason)...174
17.4.4 when(value)...174
第 18 章 服务器通信
...17518.1 自定义服务器端 ...175
18.2 安装 NodeJS...175
18.3 安装 Express ...176
18.4 调用 API ...178
18.5 使用 Amazon AWS 的无服务器应用 ....181
18.5.1 DynamoDB...181
18.5.2 简单通知服务(SNS) ...181
18.5.3 简单队列服务(SQS, Simple Queue Service) ...182
18.5.4 简单存储服务(S3) ...182
18.5.5 安全令牌服务(STS) ...182
18.6 AWSJS + Angular ...182
18.7 开始...182
18.8 介绍...184
18.9 安装...184
18.10 运行...185
18.11 用户认证/鉴权...186
18.12 UserService...190
18.13 迁移到 AWS 上 ...191
18.14 AWSService ...194
18.15 在 Dynamo 上开始...196
18.16 $cacheFactory...196
18.17 保存currentUser...197
18.18 上传到 S3...199
18.19 处理文件上传 ...201
18.20 查询 Dynamo ...203
18.21 在 HTML 显示列表 ...204
18.22 出售我们的作品 ...205
18.23 使用 Stripe ...206
18.24 使用 Firebase 的无服务器应用 ...209
18.25 使用 Firebase 和 Angular 的三方 数据绑定 ...210
18.26 从 AngularFire 开始...211
18.26.1 注册并创建一个 Firebase...211
18.26.2 包含 Firebase 和 AngularFire 库...212
18.26.3 把 Firebase 作为依赖项 添加...212
18.26.4 绑定模型到 Firebase URL.... 212
18.26.5 数据同步 ... 213
18.27 在 AngularFire 中排序... 214
18.28 Firebase 事件... 215
18.29 显式同步... 215
18.30 用 AngularFire 进行认证... 216
18.31 认证事件... 217
18.31.1 $logout()... 218
18.31.2 $createUser()... 218
18.32 使用 Firebase 托管部署你的 Angular 应用...218
18.32.1 安装 Firebase 工具... 218
18.32.2 部署你的 Web 站点... 219
18.33 除了 AngularFire 之外... 219
第 19 章 测试
... 22019.1 为什么要做测试 ... 220
19.2 测试策略... 220
19.3 开始测试... 220
19.4 AngularJS 测试的类型 ... 221
19.4.1 单元测试 ... 221
19.4.2 端到端测试 ... 222
19.5 开始... 222
19.6 初始化 Karma 配置文件 ... 223
19.7 配置选项... 226
19.8 使用 RequireJS ... 231
19.9 Jasmine... 233
19.9.1 细则套件 ... 233
19.9.2 定义一个细则 ... 233
19.10 预期... 234
19.10.1 内置的匹配器 ... 234
19.10.2 安装和卸载 ... 237
19.11 端到端的介绍 ... 238
19.11.1 选项输入 ... 244
19.11.2 重复循环元素 ... 244
19.12 模拟和测试帮助函数 ... 245
19.13 模拟$httpBackend... 246
19.14 测试一个应用 ... 251
19.14.1 测试路由 ... 252
19.14.2 测试页面内容 ... 255
19.14.3 测试控制器 ... 257
19.14.4 测试服务和工厂... 259
19.14.5 测试过滤器... 263
19.14.6 测试模板... 264
19.14.7 测试指令... 266
19.15 测试事件 ... 269
19.16 对 Angular 的持续集成... 270
19.17 Protractor ... 270
19.18 配置 ... 272
19.19 配置选项 ... 273
19.20 编写测试 ... 275
19.21 测试实践 ... 278
19.21.1 我们的应用... 278
19.21.2 测试的策略... 279
19.22 建立我们的第一个测试... 279
19.23 测试输入框 ... 281
19.23.1 测试列表... 282
19.23.2 测试路由... 284
19.24 页面对象 ... 285
第 20 章 事件
... 28720.1 什么是事件 ... 287
20.2 事件传播 ... 287
20.2.1 使用$emit来冒泡事件 ...288
20.2.2 使用$broadcast向下传递 事件 ...288
20.3 事件监听 ... 289
20.4 事件对象 ... 289
20.5 事件相关的核心服务... 290
20.5.1 核心系统的$emitted事件 ...290
20.5.2 核心系统的$broadcast 事件 ...290
第 21 章 架构
... 29221.1 目录结构 ... 292
21.2 模块 ... 293
21.3 控制器 ... 294
21.4 指令 ... 296
21.5 测试 ... 296
第 22 章 Angular 动画
... 29722.1 安装 ... 297
22.2 它是如何运作的... 297
22.3 使用 CSS3 过渡... 298
22.4 使用 CSS3 动画... 300
22.5 交错 CSS 过渡/动画... 301
22.5.1 交错 CSS 过渡... 301
22.5.2 交错 CSS 动画... 302
22.5.3 什么指令支持交错动画... 302
22.6 使用 JavaScript 动画 ... 302
22.7 微调动画 ... 303
22.8 DOM 回调事件 ... 304
22.9 内置指令的动画 ... 304
22.9.1 ngRepeat动画...304
22.9.2 ngView动画...306
22.9.3 ngInclude动画...308
22.9.4 ngSwitch动画...310
22.9.5 ngIf动画...312
22.9.6 ngClass动画...314
22.9.7 ngShow/ngHide动画 ...316
22.10 创建自定义动画 ... 318
22.10.1 addClass()... 319
22.10.2 removeClass()... 320
22.10.3 enter()... 321
22.10.4 leave()... 322
22.10.5 move()... 323
22.11 与第三方库集成 ... 324
22.11.1 Animate.css... 324
22.11.2 TweenMax/TweenLite ... 324
第 23 章 digest 循环和
$apply... 32623.1 $watch列表...326
23.2 脏值检查 ... 327
23.3 $watch... 328
23.4 $watchCollection... 330
23.5 页面中的$digest循环...330
23.6 $evalAsync列表...331
23.7 $apply... 332
23.8 何时使用$apply... 332
第 24 章 揭秘 Angular
... 33424.1 视图的工作原理 ... 335
24.1.1 编译阶段 ... 335
24.1.2 运行时 ... 336
第 25 章 AngularJS 精华扩展
... 33725.1 AngularUI ... 337
25.2 安装 ... 337
25.3 ui-router... 337
25.3.1 安装 ... 337
25.3.2 事件 ... 342
25.3.3 $stateParams... 343
25.3.4 $urlRouterProvider... 344
25.3.5 创建一个导航程序 ...345
25.4 ui-utils...346
25.4.1 安装...347
25.4.2 mask...347
25.4.3 ui-event...347
25.4.4 ui-format...348
第 26 章 移动应用
...35026.1 响应式 Web 应用...350
26.2 交互...350
26.2.1 安装...350
26.2.2 ngTouch...351
26.2.3 $swipe服务 ...352
26.2.4 angular-gestures和多点 触控手势...353
26.2.5 安装angular-gestures...354
26.2.6 使用angular-gestures...354
26.3 Cordova 中的原生应用程序...355
26.4 Cordova 入门 ...356
26.4.1 Cordova 开发流程...359
26.4.2 平台...359
26.4.3 插件...359
26.4.4 构建...360
26.4.5 模拟和运行 ...360
26.4.6 开发阶段 ...360
26.4.7 Anguar 中的 Cordova 服务 ...361
26.5 引入 Angular...362
26.6 使用 Yeoman 构建...363
26.6.1 修改 Yeoman 以便使用 Cordova ...364
26.6.2 装配 Yeoman 构建...365
26.6.3 构建移动部分 ...365
26.6.4 处理引导程序 ...367
第 27 章 本地化
...36927.1 angular-translate...369
27.2 安装...369
27.3 教你的应用一种新语言 ...370
27.4 多语言支持...371
27.5 运行时切换语言 ...372
27.6 加载语言...373
27.7 angular-gettext...374
27.8 安装...374
27.9 用法...375
27.10 字符串提取 ...375
27.11 翻译字符串 ...377
27.12 编译新语言... 378
27.13 改变语言... 379
第 28 章 缓存
... 38128.1 什么是缓存... 381
28.2 Angular 中的缓存 ... 381
28.2.1 $cacheFactory简介...381
28.2.2 缓存对象 ... 382
28.3 $http中的缓存 ...382
28.3.1 默认的$http缓存 ...382
28.3.2 自定义缓存 ... 383
28.4 为$http设置默认缓存 ...384
第 29 章 安全性
... 38529.1 严格的上下文转义:$sce服务 ...385
29.2 URL 白名单 ... 387
29.3 URL 黑名单 ... 388
29.4 $sce API ... 388
29.4.1 getTrusted... 388
29.4.2 parse... 389
29.4.3 trustAs... 389
29.4.4 isEnabled... 390
29.5 配置$sce... 390
29.6 可信赖的上下文类型 ... 390
第 30 章 AngularJS 和 IE 浏览器
... 39130.1 Ajax 缓存 ... 393
30.2 AngularJS 中的 SEO... 393
30.3 使 Angular 应用可被索引 ... 393
30.4 服务端... 393
30.4.1 hashbang 语法 ... 394
30.4.2 HTML5 路由模式 ... 394
30.5 服务端处理 SEO 的选项... 394
30.5.1 使用 Node/Express 中间件 ... 395
30.5.2 使用 Apache 重写 URL ... 395
30.5.3 使用 Ngnix 代理 URL ... 396
30.6 获取快照... 396
30.7 使用 Zombie.js 获取 HTML 快照 ... 397
30.8 使用grunt-html-snapshot... 398
30.9 Prerender.io ... 399
30.10 <noscript>方法 ...400
第 31 章 构建 Angular Chrome 应用
... 40131.1 了解 Chrome 应用 ... 401
31.1.1 manifest.json... 401
31.1.2 背景脚本 ... 401
31.1.3 视图 ... 401
31.2 构建你的 Chrome 应用 ... 402
31.3 搭建框架 ... 402
31.4 manifest.json ... 403
31.5 tab.html ... 404
31.6 在 Chrome 中加载应用... 405
31.7 主模块 ... 406
31.8 构建主页 ... 406
31.9 使用 Wundergroud 的天气 API ... 408
31.10 设置界面 ... 411
31.11 实现用户服务 ... 413
31.12 城市自动填充/自动完成... 415
31.13 添加时区支持 ... 418
第 32 章 优化 Angular 应用
... 42132.1 优化什么 ... 421
32.2 优化$digest循环...421
32.3 优化ng-repeat... 423
32.4 优化$digest调用...423
32.5 优化$watch函数...424
32.5.1 bindonce... 425
32.5.2 $watch函数的自动优化 ...427
32.6 优化过滤器 ... 427
32.6.1 不变的数据... 427
32.6.2 过滤后的数据... 427
32.7 页面加载优化技巧... 428
32.7.1 压缩 ... 429
32.7.2 利用$templateCache... 429
第 33 章 调试 AngularJS
... 43033.1 从 DOM 中调试 ... 430
33.1.1 scope()... 431
33.1.2 controller()... 431
33.1.3 injector()... 431
33.1.4 inheritedData()... 431
33.2 调试器 ... 431
33.3 Angular Batarang ... 432
33.3.1 安装 Batarang ... 432
33.3.2 检查模型... 433
33.3.3 检查性能... 433
33.3.4 检查依赖图表... 434
33.3.5 可视化应用... 434
第 34 章 下一步
... 43534.1 jqLite 和 jQuery ... 435
34.2 了解基本工具 ... 436
34.3 Grunt... 436
34.4 grunt-angular-templates... 439
34.4.1 安装 ... 439
34.4.2 用法 ... 440
34.4.3 可用选项 ... 440
34.4.4 用法 ... 442
34.5 Lineman ... 443
34.6 Bower... 445
34.6.1 安装 ... 445
34.6.2 Bower 简介 ... 445
34.6.3 配置 Bower... 446
34.6.4 搜索程序包... 447
34.6.5 安装程序包... 447
34.6.6 使用程序包... 447
34.6.7 移除程序包... 448
34.7 Yeoman... 448
34.7.1 安装 ... 448
34.7.2 用法 ... 449
34.7.3 创建路由 ... 451
34.7.4 创建控制器... 451
34.7.5 创建自定义指令... 451
34.7.6 创建自定义过滤器... 451
34.7.7 创建视图 ... 451
34.7.8 创建服务 ... 452
34.7.9 创建装饰器... 452
34.8 配置 Angular 生成器... 452
34.8.1 CoffeeScript ... 452
34.8.2 安全压缩 ... 452
34.8.3 跳过索引 ... 452
34.9 测试应用 ... 452
34.10 打包应用 ... 453
34.11 打包模板 ... 453
第 35 章 总结
... 4561 2 3 4 5
18 6 7 8 9 10 11 12 13 14 15 16 17
初识 AngularJS
本章的目标是帮助你熟悉与AngularJS有关的一些术语和技术,以及它们背后相关的工作原 理。即使以前从来没有接触过AngularJS,通过将零碎的知识点组合在一起,你也可以构建一个 属于自己的AngularJS应用。
1.1 浏览器如何获取网页
我们把互联网想象成一个邮局:当你想给朋友写信时,首先要把内容写在一张信纸上,然后 在信封上写上地址,再把信纸装进信封。
当你把信送到邮局,邮件分拣机会根据邮编和地址来判断你的朋友住在哪里。如果他住在一 栋有很多房间的公寓大楼里面,邮局会把信件投递到大楼的前台,然后大楼的工作人员会根据房 间号再次进行分拣。
互联网的工作原理和上面的过程很类似。不同的是,现实世界中由街道连接起来的楼房和公 寓,在互联网世界中被路由器和网线连接起来的计算机所取代。每一台计算机都有一个唯一的地 址,让网络可以定位到它。
多个公寓房间共享同一个街道地址,与此类似,多台计算机也可以共享同一个网络或路由器。
比如,在使用星巴克提供的免费Wi-Fi时,多台计算机就会共享同一个公网IP地址。尽管如此,
你的计算机依然可以通过路由器分配的内网IP地址被单独访问到,路由器就好比公寓大楼的工作 人员,而内网IP地址就好比房间号。
IP是互联网协议(Internet Protocol)的缩写。IP地址是为每个接入到网络中的设备 分配的数字标识符。计算机、打印机甚至手机都有自己的IP地址。
目 前 有 IPv4 和 IPv6 两 种 主 要 的 IP 地 址 类 型 , 普 遍 使 用 的 是 IPv4 地 址 , 例 如 192.168.0.199这种形式,而IPv6地址是2001:0db8:0000:0000:0000:ff00:0042:8329这种形 式的。
当你打开一个浏览器,并在地址栏输入http://google.com后,浏览器会“询问”网络(更准确 地说,是“询问”DNS服务器)google.com对应的IP地址是什么?如果DNS服务器知道你要找的 IP地址,就会将其结果返回;如果不知道,它会将请求转发给其他DNS服务器,直到在某一台DNS 服务器上找到对应的IP地址记录。在终端输入下列指令,可以观察DNS服务器的响应内容:
$ dig google.com
第 1 章
如 果 你 使 用 的 是 Mac 操 作 系 统 , 可 以 使 用 Terminal 终 端 程 序 , 它 通 常 储 存 在 /Applications/Utilities目录中。如果使用的是Windows操作系统,打开开始菜单,在运行 中输入cmd就可以打开终端了。
DNS服务器返回了你要访问的计算机的IP地址(例如找到了google.com对应的IP地址)后,
它就会向这个IP地址对应的计算机请求你要访问的页面。
每一个路径对应的网页都由不同的HTML文档组成(也有一些例外)。例如,当浏 览器请求http://google.com或http://google.com/images时,得到的HTML文档是不一样的。
现在,计算机已经知道了在哪个IP地址可以访问到http://google.com,它会向Google的服务器 请求显示这个页面所需的HTML。
当远程服务器把HTML文档发送回来后,浏览器会对文档进行渲染。渲染就是通过一系列操 作,使HTML页面按照设计之初的既定方式显示。
1.2 浏览器是什么
在介绍AngularJS之前,我们需要先了解浏览器在渲染网页的过程中都做了些什么。
目前市场上有很多不同品牌的浏览器,常见的有Chrome、Safari、Firefox和IE。它们的核心 功能基本上都是相同的:获取网页,并将它显示给用户。
浏览器获取页面对应的HTML文本,将其解析为一个在浏览器内部使用的结构,对页面的内 容进行布局,并在内容显示到屏幕上之前加上样式,所有这些工作都是在浏览器内部进行的。
作为Web开发人员,我们的工作是构造网页的结构和内容,这样浏览器才能将它们转化成对 用户来说比较美观的形式。
使用AngularJS,不仅可以构建页面的结构,而且可以构建用户和Web应用之间的交互。
1.3 AngularJS 是什么
AngularJS的官方文档是这样介绍它的。
完全使用JavaScript编写的客户端技术。同其他历史悠久的Web技术(HTML、CSS 和JavaScript)配合使用,使Web应用开发比以往更简单、更快捷。
AngularJS主要用于构建单页面Web应用。它通过增加开发人员和常见Web应用开发任务之间 的抽象级别,使构建交互式的现代Web应用变得更加简单。
AngularJS的开发团队将其描述为一种构建动态Web应用的结构化框架。
AngularJS使开发Web应用变得非常简单,同时也降低了构建复杂应用的难度。它提供了开发 者在现代Web应用中经常要用到的一系列高级功能,例如:
解耦应用逻辑、数据模型和视图;
Ajax服务;
1 2 3 4 5
18 6 7 8 9 10 11 12 13 14 15 16 17
依赖注入;
浏览历史(使书签和前进、后退按钮能够像在普通Web应用中一样工作);
测试;
更多功能。
1.3.1 AngularJS有什么不同
在其他JavaScript框架中,我们被迫从自定义的JavaScript对象中进行扩展,并从外到内操作 DOM。以jQuery
①为例,为了在DOM中插入一个按钮元素,我们必须知道要把元素放到何处,并 在合适的位置插入它:
var btn = $("<button>Hi</button>");
btn.on('click', function(evt) { console.log("Clicked button"); });
$("#checkoutHolder").append(btn);
尽管这个过程并不复杂,但是它要求开发者对整个DOM结构都有所了解,并强迫我们在 JavaScript代码中加入复杂的控制逻辑,用以操作外部DOM。
而AngularJS则通过原生的Model-View-Controller(MVC,模型视图控制器)功能增强了 HTML。结果表明,这个选择可以快捷和愉悦地构建出令人印象深刻并且极富表现力的客户端 应用。
利用它,开发者可将页面的一部分封装为一个应用,并且不强迫整个页面都使用AngularJS进 行开发。这个特质在某些情况下非常有用,比如你的工作流程中已经包含了另外一个框架,或者你 只希望页面中的某一部分是动态的,而剩下的部分是静态的或者是由其他JavaScript框架来控制的。
此外,AngularJS团队非常重视框架文件压缩后的大小,这样使用它就不会付出太多的额外 代价(写作本书时,文件压缩后的体积在90 KB左右)。这一特性使得AngularJS非常适合用于开 发功能原型。
1.3.2 许可
AngularJS的源码托管在GitHub
②上,可以免费获取。它基于MIT许可发布,这意味着你可以 为AngularJS贡献代码,使其变得更加优秀。
为了促进大家为AngularJS贡献代码,开发团队把开发流程变得相对开放。任何重大变化都 需要在AngularJS的邮件列表上
③进行讨论,所有人都可以加入讨论,这样一来大家就可以对潜在 的变动进行改进,并且防止重复劳动。
关于贡献代码的更多内容可以在AngularJS的官网中查看“贡献代码”部分
④。
——————————
① http://jquery.com/
② http://github.com
③ https://groups.google.com/forum/?hl=en#!forum/angular
④ http://docs.angularjs.org/misc/contribute
数据绑定和第一个 AngularJS Web应用
Hello World
写一个Hello World应用是开始学习AngularJS的最基本途径,让我们从一段简单得不能再简单 的HTML开始吧。
随着学习的深入,我们会逐渐深入到AngularJS的内部原理中。现在,让我们先来写一个Hello World应用。
<!DOCTYPE html>
<html ng-app>
<head>
<title>Simple app</title>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.13/angular.js">
</script>
</head>
<body>
<input ng-model="name" type="text" placeholder="Your name">
<h1>Hello {{ name }}</h1>
</body>
</html>
图2-1 Hello World
虽然这个例子不怎么有趣,但它展示了AngularJS最基本也最令人印象深刻的功能之一:数 据绑定。
第 2 章
1 2 3 4 5
18 6 7 8 9 10 11 12 13 14 15 16 17
请注意,在本章的示例代码中为了方便展示第一个核心概念,我们并没有用最 佳方式编写控制器。这也是本书唯一一处我们不鼓励将示例代码应用在实际生 产中,而只作为学习范例的地方。
2.1 AngularJS 中的数据绑定
在Rails等传统Web框架中,控制器将多个模型中的数据和模板组合在一起形成视图,并将其 提供给用户,这个组合过程会产生一个单向视图。如果没有创建任何自定义的JavaScript组件,视 图只会体现它渲染时模型暴露出的数据。在写这篇文章时,已经出现了好几个可以在视图和模型 之间自动进行数据绑定的框架。
AngularJS则采用了完全不同的解决方案。它创建实时模板来代替视图,而不是将数据合并 进模板之后更新DOM。任何一个独立视图组件中的值都是动态替换的。这个功能可以说是 AngularJS中最重要的功能之一,也是让我们只用10行代码,并且在没有任何JavaScirpt的情况下 就可以写出Hello World的关键。
要实现这个功能,只要在HTML页面中引用 angular.js ,并在某个DOM元素上明确设置 ng-app 属性即可。 ng-app 属性声明所有被其包含的内容都属于这个AngularJS应用,这也是我们 可以在Web应用中嵌套AngularJS应用的原因。只有被具有 ng-app 属性的DOM元素包含的元素才 会受AngularJS影响。
视图中的插值会在计算一个或多个变量时被动态替换,替换结果是字符串中的 插值被变量的值替代。
例如,如果有一个叫做 name 的变量,它的值是“Ari”,那么视图中的 "Hello {{ name }}" 字符串会被替换成“Hello Ari”。
自动数据绑定使我们可以将视图理解为模型状态的映射。当客户端的数据模型发生变化时,
视图就能反映出这些变化,并且不需要写任何自定义的代码,它就可以工作。
在MVC(Model View Controller,模型视图控制器)的世界里,控制器可以不必担心会牵 扯到渲染视图的工作。这样我们就不必再担心如何分离视图和控制器逻辑,并且也可以使测试变 得既简单又令人愉悦。
MVC是一种软件架构设计模式,它将表现从用户交互中分离出来。通常来讲,
模型中包含应用的数据和与数据进行交互的方法,视图将数据呈献给用户,而 控制器则是二者之间的桥梁。
这种表现分离
①能将应用中的对象很好地隔离开来,因此视图不需要知道如何保 存对象,只要知道如何显示它即可。这也意味着数据模型不需要同视图进行交 互,只需要包含数据和操作视图的方法。控制器用来存放将二者绑定在一起的 业务逻辑。
——————————
① http://martinfowler.com/eaaDev/uiArchs.html
AngularJS
①会记录数据模型所包含的数据在任何特定时间点的值(在Hello World例子中就是 name 的值),而不是原始值。
当AngularJS认为某个值可能发生变化时,它会运行自己的事件循环来检查这个值是否变
“脏”。如果该值从上次事件循环运行之后发生了变化,则该值被认为是“脏”值。这也是Angular 可以跟踪和响应应用变化的方式。
这个事件循环会调用 $digest() 循环,第23章将会详细介绍。
这个过程被称作脏检查(dirty checking)。脏检查是检查数据模型变化的有效手段。当有潜 在的变化存在时,AngularJS会在事件循环时执行脏检查(第24章会深入讨论)来保证数据的一 致性。
如果使用 KnockoutJS 这种通过在数据模型上绑定事件监听器来监听数据变化的框架,这个
过程会变得更复杂且低效
②。处理事件合并、依赖跟踪和大量的事件触发(event firing)是非常复 杂的,而且会在性能方面导致额外的问题。
尽管存在更高效的方式,但脏检查可以运行在所有浏览器中并且是可预测的。
此外,很多在速度和效率方面有要求的软件都会使用脏检查的方案
③。
借助AngularJS,不需要构建复杂和新的JavaScript功能,就可以在视图中实现类自动同步的 机制。
为了表示内部和内置的库函数,Angular使用 $ 预定义对象。尽管这类似于全局的jQuery 对象 $ ,但它们是完全无关的。只要遇到 $ 符号,你都可以只把它看作一个Angular对象。
2.2 简单的数据绑定
审阅一下上面写的代码,我们使用 ng-model 指令将内部数据模型对象( $scope )中的 name 属性绑定到了文本输入字段上。
这意味着无论在文本输入字段中输入了什么,都会同步到数据模型中。
数据模型对象(model object)是指 $scope 对象。 $scope 对象是一个简单的 JavaScript对象,其中的属性可以被视图访问,也可以同控制器进行交互。如果 不理解这个概念也没有关系,后面的例子将会对这个概念进行详细说明。
双向数据绑定(bi-directional)意味着如果视图改变了某个值,数据模型会通过 脏检查观察到这个变化,而如果数据模型改变了某个值,视图也会依据变化重 新渲染。
在输入字段上使用 ng-model 指令来实现数据绑定,如下所示:
——————————
① http://angularjs.org
② 这有些言过其实,低效是真,复杂未必。——译者注
③ 比如在游戏开发中就大量使用脏检查技术。——译者注
1 2 3 4 5
18 6 7 8 9 10 11 12 13 14 15 16 17
<input ng-model="person.name" type="text" placeholder="Yourname">
<h1>Hello{{ person.name }}</h1>
这样绑定就设置好了(没错,就是这么简单)。我们可以观察一下视图是如何更新数据模型 的。当输入字段中的值发生变化时, person.name 会被更新,而视图将反映出这个更新。
我们仅通过视图就实现了一个双向数据绑定。为了从其他角度(后端到前端)解释双向数据 绑定,后面会深入介绍控制器。
正 如 ng-app 声 明 所 有 被 它 包 含 的 元 素 都 属 于 AngularJS 应 用 一 样 , DOM 元 素 上 的
ng-controller 声明所有被它包含的元素都属于某个控制器。
为了解释这个概念,我们将前面的例子修改成如下的样子:
<div ng-controller='MyController'>
<input ng-model="person.name" type="text" placeholder="Your name">
<h1>Hello {{ person.name }}</h1>
</div>
在这个例子中,我们会创建一个每秒钟走一步的时钟(时钟通常都是这样的),并更新 clock 变量上的数据:
function MyController($scope, $timeout) { var updateClock = function() {
$scope.clock = new Date();
$timeout(function() { updateClock();
}, 1000);
};
updateClock();
};
在这个例子中, MyController 函数接受两个参数,即该DOM元素的 $scope 和
$timeout 。第13章将介绍如何使用不同的变量定义函数。
在这个例子中,当定时器触发时会调用 updateClock() 函数,将 $scope.clock 的值设置为当 前时间。
$timeout 对象
可以在视图中将 clock 变量用 {{ }} 包起来,以显示 $scope 中的 clock 的值:
<div ng-controller="MyController">
<h5>{{ clock }}</h5>
</div>
下面是完整的示例代码:
<!doctype html>
<html ng-app>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.13/angular.js"></script>
</head>
<body>
<div ng-controller="MyController">
<h1>Hello {{ clock }}!</h1>
</div>
<script type="text/javascript">
function MyController($scope, $timeout) { var updateClock = function() { $scope.clock = new Date();
$timeout(function() { updateClock();
}, 1000);
};
updateClock();
};
</script>
</body>
</html>
在线示例:http://jsbin.com/uHiVOZo/1/edit?html,output。
尽管我们可以将所有代码都写在一个文件中,但由于需要将不同的组件分开开 发,将代码写在一个文件中会使协同工作变得非常困难。通常情况下,更好的 选择是将JavaScript放在单独的文件中,而不是index.html中。
上面的代码可以修改成:
<!doctype html>
<html ng-app>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.13/angular.js"></script>
</head>
<body>
<div ng-controller="MyController">
<h1>Hello {{ clock }}!</h1>
</div>
<script type="text/javascript" src="js/app.js"></script>
</body>
</html>
将前面例子中的JavaScript代码放在js/app.js文件中,而不是将它直接写在HTML中。
// 在app.js中
function MyController($scope, $timeout) { var updateClock = function() {
$scope.clock = new Date();
$timeout(function() { updateClock();
}, 1000);
};
updateClock();
};
2.3 数据绑定的最佳实践
由于JavaScript自身的特点,以及它在传递值和引用时的不同处理方式,通常认为,在视图中
通过对象的属性而非对象本身来进行引用绑定,是Angular中的最佳实践。
1 2 3 4 5
18 6 7 8 9 10 11 12 13 14 15 16 17
如果把这个最佳实践应用到上面时钟的例子中,需要把视图中的代码改写成下面这样:
<!doctype html>
<html ng-app>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.13/angular.js"></script>
</head>
<body>
<div ng-controller="MyController">
<h1>Hello {{ clock.now }}!</h1>
</div>
<script type="text/javascript" src="js/app.js"></script>
</body>
</html>
在这个例子中,相比每秒钟都更新 $scope.clock ,更新 clock.now 的值会是更好的选择。有 了这个优化后,我们将反映数据变化的逻辑做如下修改:
// 在app.js中
function MyController($scope) { $scope.clock = {
now: new Date() };
var updateClock = function() { $scope.clock.now = new Date() };
setInterval(function() { $scope.$apply(updateClock);
}, 1000);
updateClock();
};
将所有绑定都通过这样的形式放在视图中,是个非常好的主意。
模 块
在JavaScript中,将函数代码全部都定义在全局命名空间中绝对不是什么好主意,这样做会导 致冲突从而使调试变得非常困难,浪费宝贵的开发时间。
上一章介绍数据绑定时,我们把控制器的代码写到了一个在全局命名空间中定义的函数里:
function MyController($scope) { var updateClock = function() { $scope.clock = new Date();
};
setInterval(function() { $scope.$apply(updateClock);
}, 1000);
updateClock();
};
本章将讨论如何写出高效、能用在生产环境中的控制器代码,并把它封装在一个我们称之为 模块(module)的单元内。
在AngularJS中,模块是定义应用的最主要方式。模块包含了主要的应用代码。一个应用可 以包含多个模块,每一个模块都包含了定义具体功能的代码。
使用模块能给我们带来许多好处,比如:
保持全局命名空间的清洁;
编写测试代码更容易,并能保持其清洁,以便更容易找到互相隔离的功能;
易于在不同应用间复用代码;
使应用能够以任意顺序加载代码的各个部分。
AngularJS允许我们使用 angular.module() 方法来声明模块,这个方法能够接受两个参数,
第一个是模块的名称,第二个是依赖列表,也就是可以被注入到模块中的对象列表。
angular.module('myApp', []);
这个方法相当于AngularJS模块的setter方法,是用来定义模块的。
调用这个方法时如果只传递一个参数,就可以用它来引用模块。例如,可以通过以下代码来 引用 myApp 模块:
// 这个方法用于获取应用 angular.module('myApp')
第 3 章
1 2 3 4 5
18 6 7 8 9 10 11 12 13 14 15 16 17
这个方法相当于AngularJS模块的getter方法,用来获取对模块的引用。
接下来,就可以在 angular.module('myApp') 返回的对象上创建我们的应用了。
开发大型应用时,我们会创建多个模块来承载业务逻辑。将复杂的功能分割成不同的模块,
有助于单独为它们编写测试,相关信息参见第21章。
3.1 参数
下面是 angular.module() 的参数列表。
3.1.1 name (字符串)
name 是模块的名称,字符串变量。
3.1.2 requires (字符串数组)
requires 包含了一个字符串变量组成的列表,每个元素都是一个模块名称,本模块依赖于这
些模块,依赖需要在本模块加载之前由注入器进行预加载。
作用域
作用域(scope)
①是构成AngularJS应用的核心基础,在整个框架中都被广泛使用,因此了解 它如何工作是非常重要的。
应用的作用域是和应用的数据模型相关联的,同时作用域也是表达式执行的上下文。 $scope 对象是定义应用业务逻辑、控制器方法和视图属性的地方。
作用域是视图和控制器之间的胶水。在应用将视图渲染并呈献给用户之前,视图中的模板会 和作用域进行连接,然后应用会对DOM进行设置以便将属性变化通知给AngularJS。这个功能让 XHR请求等promise对象的实现变得非常容易。查看第17章获取更多关于promise对象的内容。
作用域是应用状态的基础。基于动态绑定,我们可以依赖视图在修改数据时立刻更新 $scope , 也可以依赖 $scope 在其发生变化时立刻重新渲染视图。
AngularJS将 $scope 设计成和DOM类似的结构,因此 $scope 可以进行嵌套,也就是说我们可 以引用父级 $scope 中的属性。
如果你了解JavaScript,对这个分层的概念应该并不陌生。在JavaScript中,当创建 一个新的执行上下文时,实际上是用函数创建了一个新的本地上下文。AngularJS中
$scope 的概念与其类似,当为子DOM元素创建新的作用域时,实际上是为子DOM元素
创建了一个新的执行上下文。
作用域提供了监视数据模型变化的能力。它允许开发者使用其中的 apply 机制,将数据模型 的变化在整个应用范围内进行通知。我们在作用域的上下文中定义和执行表达式,同时它也是将 事件通知给另一个控制器和应用其他部分的中介。
将应用的业务逻辑都放在控制器中,而将相关的数据都放在控制器的作用域中,这是非常完 美的架构。
4.1 视图和$scope的世界
AngularJS启动并生成视图时,会将根 ng-app 元素同 $rootScope 进行绑定。 $rootScope 是所 有 $scope 对象的最上层。
——————————
① 如非特别强调,本书中的作用域均指AngularJS中的作用域对象,而不是JavaScript作用域。——译者注
第 4 章
1 2 3 4 5
18 6 7 8 9 10 11 12 13 14 15 16 17
$rootScope 是AngularJS中最接近全局作用域的对象。在 $rootScope 上附加太多业 务逻并不是好主意,这与污染JavaScript的全局作用域是一样的。
$scope 对象就是一个普通的JavaScript对象,我们可以在其上随意修改或添加属性。
$scope 对象在AngularJS中充当数据模型,但与传统的数据模型不一样, $scope 并不负责处 理和操作数据,它只是视图和HTML之间的桥梁,它是视图和控制器之间的胶水。
$scope 的所有属性,都可以自动被视图访问到。假设我们有如下的HTML:
<div ng-app="myApp">
<h1>Hello {{ name }}</h1>
</div>
我们希望 {{ name }} 变量是本地 $scope 的一个属性,效果如图4-1所示。
angular.module('myApp', []) .run(function($rootScope) { $rootScope.name = "World";
});
图4-1 简单的$rootScope绑定
4.2 就是 HTML 而已
我们的应用负责渲染HTML并将它交给浏览器来显示。这个HTML中包含了各种标准的 HTML 元 素 , 包 括 AngularJS特 有 的 以 及 非 AngularJS特 有 的 元 素 。 AngularJS不 会 对 不 包 含 AngularJS特殊声明的元素进行任何处理。
<h2>Hello world</h2>
<h3>Hello {{ name }}</h3>
上面这个例子中,AngularJS不会处理 <h2> 元素,但是会在作用域发生变化时更新 <h3> 元素。
我们可以在AngularJS应用的模板中使用多种标记,包括下面这些。
指令:将DOM元素增强为可复用的DOM组件的属性或元素。
值绑定:模板语法 {{ }} 可以将表达式绑定到视图上。
过滤器:可以在视图中使用的函数,用来进行格式化。
表单控件:用来检验用户输入的控件。
4.3 作用域能做什么
作用域有以下的基本功能:
提供观察者以监视数据模型的变化;
可以将数据模型的变化通知给整个应用,甚至是系统外的组件;
可以进行嵌套,隔离业务功能和数据;
给表达式提供运算时所需的执行环境。
开发AngularJS应用的大部分工作内容,就是构建作用域及其相关的功能。
作用域包含了渲染视图时所需的功能和数据,它是所有视图的唯一源头。可以将作 用域理解成视图模型(view model)。
前面的例子中,我们在 $rootScope 中设置了一个 name 变量并在视图中引用了它:
angular.module('myApp', []) .run(function($rootScope) { $rootScope.name = "World";
});
在视图中可以引用这个 name 属性并将它展示给用户:
<div ng-app="myApp">
<h1>Hello {{ name }}</h1>
</div>
我们可以不将变量设置在 $rootScope 上,而是用控制器显式创建一个隔离的子 $scope 对象,
把它设置到这个子对象上。使用 ng-controller 指令可以将一个控制器对象附加到DOM元素上,
如下所示:
<div ng-app="myApp">
<div ng-controller="MyController">
<h1>Hello {{ name }}</h1>
</div>
</div>
我们可以创建一个控制器来管理与其相关的变量,而不用将 name 变量直接放在 $rootScope 上:
angular.module("myApp", []) .controller('MyController', function($scope) {
$scope.name = "Ari";
});