本项目源码放在githubhtml
截止到这部分,咱们的BooksComponent
组件获取和显示的都是本地模拟的数据。
接下来咱们要开始对这些进行重构,让聚焦于为它的视图提供支持,这也让它更容易使用模拟服务进行单元测试。前端
咱们不该该让组件来直接获取或保存数据,它们应该聚焦于展现数据,而数据访问的工做交给其余服务来作。
这里咱们须要建立一个名为BooksService
的服务,让咱们应用中全部的类都使用它来获取书本列表的数据,使用的时候,只须要将它经过Angular的依赖注入机制注入到须要用的组件的构造函数中。git
知识点:
服务能够实现多个不一样组件之间信息共享,后面咱们还会将它注入到两个地方:
BooksService
中,使用该服务发送消息。
IndexService
中,使用该服务来展现消息。github
接下来咱们使用命令行,建立BooksService
:编程
ng g service books
复制代码
在生成的books.service.ts
文件中:数组
// books.service.ts
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
复制代码
新导入了@Injectable
装饰器,是为了让BooksService
提供一个可注入的服务,而且它还能够拥有本身的待注入的依赖,简单理解就是若是你的服务须要依赖,那么你就须要导入它。
而且它接收该服务的元数据对象。bash
接下来咱们开始编写books.service.ts
服务。服务器
这里咱们导入Books
和BookList
,并添加一个getBooks
方法来返回全部书本的数据,而且还须要添加一个getBooks
方法来返回指定id的书本信息:微信
// index.component.ts
import { Books } from './books';
import { BookList } from './mock-books';
@Injectable({
providedIn: 'root'
})
export class BooksService {
constructor() { }
getBookList(): Books[] {
return BookList;
}
getBook(id: number): Books{
return BookList.find(book => book.id === id)
}
}
复制代码
在咱们使用这个服务以前,须要先注册该服务,由于咱们在使用ng g service books
命令建立服务时,CLI已经默认为咱们添加了注册了,这是方法就是上面代码中的:并发
providedIn: 'root'
复制代码
表示将咱们的服务注册在根注入器上,这样咱们就能够把这个服务注入到任何享用的类上了。
IndexComponent
先删除BookList
的引入,并修改books
属性的定义:
// index.component.ts
import { BooksService } from '../books.service';
export class IndexComponent implements OnInit {
books : Books[];
ngOnInit() {}
}
复制代码
而后注入咱们的BooksService
服务,须要先往构造函数中添加一个私有的booksservice
,使用注入的BooksService
做为类型,理解成一个注入点:
// index.component.ts
constructor(private booksservice: BooksService) { }
复制代码
以后咱们须要添加一个getBooks
方法来获取这些书本数据,并在生命周期函数ngOnInit
中调用:
export class IndexComponent implements OnInit {
ngOnInit() {
this.getBooks();
}
getBooks(): void{
this.books = this.booksservice.getBookList();
}
}
复制代码
DetailComponent
<!-- detail.component.html -->
<div *ngIf="books" class="detail">
<h3>《{{books.title}}》介绍</h3>
<div>
<img src="{{books.url}}">
</div>
<p>书本标题: {{books.title}}</p>
<p>书本做者: {{books.author}}</p>
<p>书本id: {{books.id}}</p>
</div>
<div *ngIf="!books" class="detail">
<h3>暂无信息</h3>
</div>
复制代码
知识点:
这里使用了*ngIf
指令,当条件为true
则显示其HTML内容。
// detail.component.ts
import { Books } from '../books';
import { BooksService } from '../books.service';
export class DetailComponent implements OnInit {
constructor(
private route: ActivatedRoute,
private location: Location,
private booksservice: BooksService // 引入BooksService服务
) { }
books: Books; // 定义books类型
ngOnInit() {
this.getDetail()
}
getDetail(): void{
const id = +this.route.snapshot.paramMap.get('id');
this.getBooks(id);
}
getBooks(id: number): void {
this.books = this.booksservice.getBook(id);
}
}
复制代码
这段代码,主要定义了getBooks
方法,当刚进入页面时,将书本id
传入getBooks
方法,去BooksService
去获取对应id的书本信息,并复制给变量books
,而后展现到页面。
改造以后,咱们的页面显示依旧正常。
可是咱们要知道,这背后的逻辑已经改变了。
这里简单介绍关键概念,具体能够查看 RxJS 官网,也能够参考 浅析Angular之RxJS。
RxJS全称Reactive Extensions for JavaScript
,中文意思: JavaScript的响应式扩展。
RxJS主要是提供一种更增强大和优雅的方式,来利用响应式编程的模式,实现JavaScript的异步编程。
RxJS 是基于观察者模式和迭代器模式以函数式编程思惟来实现的。RxJS 中含有两个基本概念:Observables
与 Observer
。
Observables
做为被观察者,是一个值或事件的流集合;而 Observer
则做为观察者,根据 Observables
进行处理。它们之间的订阅发布关系(观察者模式) 以下:
订阅:Observer
经过 Observable
提供的 subscribe()
方法订阅 Observable
。
发布:Observable
经过回调 next
方法向 Observer
发布事件。
———— 来源Angular修仙之路 RxJS Observable
另外这里列出来一些核心,具体仍是看官网咯,而且下面使用到的时候会具体介绍。
Observable
(可观察对象): 表示一个概念,这个概念是一个可调用的将来值或事件的集合。Observer
(观察者): 一个回调函数的集合,它知道如何去监听由 Observable
提供的值。Subscription
(订阅): 表示 Observable
的执行,主要用于取消 Observable
的执行。Operators
(操做符): 采用函数式编程风格的纯函数 (pure function
),使用像 map
、filter
、concat
、flatMap
等这样的操做符来处理集合。Subject
(主体): 至关于 EventEmitter
,而且是将值或事件多路推送给多个 Observer
的惟一方式。Schedulers
(调度器): 用来控制并发而且是中央集权的调度员,容许咱们在发生计算时进行协调,例如 setTimeout
或requestAnimationFrame
或其余。在咱们的真实应用中,咱们必需要等到服务器响应后,咱们才能获取到数据,所以这天生就须要用异步思惟来操做。
因为Angular中已经自带RxJS,因此咱们只要在须要使用的时候,引入便可使用:
了解完RxJS的一些概念后,咱们开始改造下这些书本的数据获取方式。
BooksService
首先咱们从 RxJS 中导入 Observable
和 of
符号:
// books.service.ts
import { Observable, of } from 'rxjs';
复制代码
知识点:
Observable
: 观察者模式中的观察者,具体能够参考 Angular修仙之路 RxJS Observable
of
: 用来获取观察者拿到的数据,一般是一个Observable
。
而后修改getBookList
方法
// books.service.ts
getBookList(): Observable<Books[]> {
return of(BookList);
}
复制代码
这里 of(BookList)
返回一个Observable<Books[]>
,它会发出单个值,这个值就是这些模拟书本的数组。
IndexComponent
这里也要修改getBooks
方法,使用subscribe
去订阅服务返回回来的值:
// index.component.ts
getBooks(): void{
this.booksservice.getBookList()
.subscribe(books => this.books = books);
}
复制代码
因为本来直接赋值数据,在实际场景中是不可能这样同步的,因此这里subscribe
函数,会在Observable
发出数据之后,再把书本列表传到里面的回调函数,再复制给books
属性。
使用这种异步方式,当 BooksService
从远端服务器获取英雄数据时,不用担忧还没拿到数据就执行后面。
下一步,咱们就要改造一下项目了。
本部份内容到这结束
Author | 王平安 |
---|---|
pingan8787@qq.com | |
博 客 | www.pingan8787.com |
微 信 | pingan8787 |
每日文章推荐 | https://github.com/pingan8787/Leo_Reading/issues |
JS小册 | js.pingan8787.com |
微信公众号 | 前端自习课 |