本文共 4308 字,大约阅读时间需要 14 分钟。
本节书摘来自华章出版社《Angular从零到一》一书中的第3章,第3节,作者王芃,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
3.3 建立模拟Web服务和异步操作
实际开发中,我们的service是要和服务器API进行交互的,而不是现在这样简单地操作数组。但问题来了,现在没有Web服务,难道真要自己开发一个吗?答案是可以做个假的,假作真时真亦假。我们在开发过程中经常会遇到这类问题,等待后端开发的进度是很痛苦的。所以Angular内建提供了一个可以快速建立测试用Web服务的方法:内存 (in-memory)服务器。
3.3.1 构建数据模型
一般来说,你需要知道自己对服务器的期望是什么,期待它返回什么样的数据,有了这个数据,我们就可以自己快速地建立一个内存服务器。拿这个例子来看,我们可能需要一个这样的对象:
对应的JSON应该是这样的:
首先我们需要安装angular-in-memory-web-api,输入npm install --save angular-in-memory-web-api,然后在Todo文件夹下创建一个文件
src\app\todo\todo-data.ts:import { InMemoryDbService } from 'angular-in-memory-web-api';import { Todo } from './todo.model';export class InMemoryTodoDbService implements InMemoryDbService { createDb() { let todos: Todo[] = [ {id: "f823b191-7799-438d-8d78-fcb1e468fc78", desc: 'Getting up', completed: true}, {id: "c316a3bf-b053-71f9-18a3-0073c7ee3b76", desc: 'Go to school', completed: false} ]; return {todos}; }}
3.3.2 实现内存Web服务
可以看到,我们创建了一个实现InMemoryDbService的内存数据库,这个数据库其实也就是把数组传入进去。接下来,我们要更改src\app\app.module.ts,加入类引用和对应的模块声明:
然后在imports数组中紧挨着HttpModule加上:
现在我们在service中试着调用我们的“假Web服务”
上面的代码中定义了一个
可能会问这个是怎么来的?分两部分看,api/todos中前面的api定义成什么都可以,但后面这个todos是有讲究的,我们回去看一下src\app\todo\todo-data.ts返回的return {todos},这个其实是return {todos: todos}的省略表示形式,如果我们不想让这个后半部分是todos,我们可以写成{nahnahnah: todos}。这样,我们改写成api_url = 'blablabla/nahnahnah'也无所谓,因为这个内存Web服务的机理是拦截Web访问,也就是说,随便什么地址都可以,内存Web服务会拦截这个地址并解析你的请求是否满足RESTful API的要求。
3.3.3 内存服务器提供的Restful API
简单来说,RESTful API中以“名词”来标识资源,比如todos;以“动词”标识操
作,比如:GET请求用于查询,PUT用于更新,DELETE用于删除,POST用于添加。比如,如果url是api/todos,那么:
查询所有待办事项:以GET方法访问api/todos。
查询单个待办事项:以GET方法访问api/todos/id,比如id是1,那么访问api/todos/1。
更新某个待办事项:以PUT方法访问api/todos/id。
删除某个待办事项:以DELETE方法访问api/todos/id。
增加一个待办事项:以POST方法访问api/todos。
在service的构造函数中我们注入了HTTP,而Angular的HTTP封装了大部分我们需要的方法,比如例子中增加一个Todo,我们就调用this.http.post(url, body, options),上面代码中.post(this.api_url, JSON.stringify(todo), {headers: this.headers})的含义是:构造一个POST类型的HTTP请求,其访问的url是this.api_url,request的body是一个JSON(把Todo对象转换成JSON),在参数配置中我们配置了request的header。
这个请求发出后返回的是一个Observable(可观察对象),我们把它转换成Promise,然后处理res(Http Response)。Promise提供异步的处理,注意到then中的写法,这个和传统编程写法不大一样,它叫做lamda表达式,相当于一个匿名函数,(input parameters) => expression,=>前面的是函数的参数,后面的是函数体。
还要一点需要强调的是:在用内存Web服务时,一定要注意res.json().data中的data属性必须要有,因为内存Web服务在返回的json中加了data对象,你真正要得到的json在这个data里面。
下一步我们来更改Todo组件的addTodo方法以便可以使用我们新的异步http方法。
这里,前半部分应该还是好理解的:this.service.addTodo(this.desc)调用service的对应方法而已,但后半部分用来做什么?...这个貌似省略号的东西是ES7中计划提供的Object Spread操作符,它的功能是将对象或数组“打散,拍平”。这么说可能还是不清晰,下面举个例子:
所以,上面的this.todos = [...this.todos, todo];相当于为todos增加一个新元素,这和push很像,那为什么不用push呢?因为这样构造出来的对象是全新的,而不是引用的,在现代编程中一个明显的趋势是不要在过程中改变输入的参数。还有就是这样做会带给我们极大的便利性和编程的一致性。下面通过给该例子添加几个功能,我们来一起体会一下。
3.3.4 Angular 2内建的HTTP方法
首先更改
上面的代码中可以看到对应Restful API的各个“动词”,Angular 2.x 提供了一系列对应名称的方法,非常简单易用。比如说在deleteTodoById方法中,我们要访问的API是/todos/:id,使用的HTTP方法是DELETE,那么我们就使用this.http.delete(url, {headers: this.headers})。
3.3.5 JSONP和CORS
除了提供HTTP方法,在同一个Module中(HttpModule)我们还能看到有JSONP。那么JSONP是什么呢?简单来说,由于浏览器的限制,JavaScript进行跨域的HTTP资源请求是不允许的。比如你自己的服务器域名是foo.bar,那么在你域名下host的JavaScript如果要请求boo.bar域名下的某个API,其本应返回json,但你的JavaScript可能出现无法访问的情况。这是因为浏览器出于安全的考虑,限制了不同源的资源请求。
但很快有人发现Web页面上调用js文件时则不受是否跨域的影响,而且发现凡是拥有src这个属性的标签都拥有跨域的能力,比如<script>、<img>、<iframe>。跨域访问数据就存在一种可能,那就是在远程服务器上设法把数据装进js格式的文件里,供客户端调用和进一步处理。
JSON在JavaScript中有良好的支持,而且JSON可以简洁地描述复杂数据结构,所以在客户端几乎可以随心所欲地处理这种格式的数据。Web客户端通过与调用脚本一模一样的方式,来调用跨域服务器上动态生成的js格式文件(一般以JSON为后缀)。显而易见,服务器之所以要动态生成JSON文件,目的就在于把客户端需要的数据装入进去。
这样逐渐形成了一种非正式传输协议,这就是JSONP了,该协议的一个要点就是允许用户传递一个callback参数给服务器端,然后服务器端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
Angular提供了JSONP对象,同时提供很多和HTTP类似的方法便于大家使用JSONP解决跨域问题。
当然,这个问题目前其实有比JSONP更好的解决方案,那就是CORS(跨来源资源共享),这是个正式浏览器技术的标准。提供了Web服务从不同网域传来沙盒脚本的方法,以避开浏览器的同源策略,是JSONP模式的现代版。与JSONP不同,CORS 除了GET要求方法以外也支援其他的HTTP要求。大部分现代浏览器都已经支持CORS。当然,JSONP可以在不支援CORS的老旧浏览器上运作。
3.3.6 页面展现更新src\app\todo\todo.component.ts,调用新的service中的方法。有趣的是,利用Object Spread操作符,我们看到代码风格更一致,逻辑也更容易理解了:
更新组件的css样式: src\app\todo\todo.component.css 和 src\styles.css,这两个文件比较大,可以到下面列出的本节代码中去查看。
其中src\app\todo\todo.component.css有一段代码稍微讲一下,这段代码把复选框原本的方块替换成SVG格式的图片,以便实现比较炫酷的效果:
...
现在我们看看成果吧,现在好看多了,如图 3.5所示。
图3.5 带样式的待办事项
本章代码:https://github.com/wpcfan/awesome-tutorials/tree/chap03/angular2/ng2-tut
打开命令行工具使用 git clone https://github.com/wpcfan/awesome-tutorials 下载。然后键入git checkout chap03切换到本章代码。
转载地址:http://vtgvl.baihongyu.com/