截止到這部分,咱們的BooksComponent
組件獲取和顯示的都是本地模擬的數據。
接下來咱們要開始對這些進行重構,讓聚焦於爲它的視圖提供支持,這也讓它更容易使用模擬服務進行單元測試。html
咱們不該該讓組件來直接獲取或保存數據,它們應該聚焦於展現數據,而數據訪問的工做交給其餘服務來作。
這裏咱們須要建立一個名爲BooksService
的服務,讓咱們應用中全部的類都使用它來獲取書本列表的數據,使用的時候,只須要將它經過Angular的依賴注入機制注入到須要用的組件的構造函數中。前端
知識點:
服務能夠實現多個不一樣組件之間信息共享,後面咱們還會將它注入到兩個地方:
BooksService
中,使用該服務發送消息。
IndexService
中,使用該服務來展現消息。git
接下來咱們使用命令行,建立BooksService
:github
ng g service books
在生成的books.service.ts
文件中:web
// books.service.ts import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' })
新導入了@Injectable
裝飾器,是爲了讓BooksService
提供一個可注入的服務,而且它還能夠擁有本身的待注入的依賴,簡單理解就是若是你的服務須要依賴,那麼你就須要導入它。
而且它接收該服務的元數據對象。編程
接下來咱們開始編寫books.service.ts
服務。數組
這裏咱們導入Books
和BookList
,並添加一個getBooks
方法來返回全部書本的數據,而且還須要添加一個getBooks
方法來返回指定id的書本信息:bash
// 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 |
微信公衆號 | 前端自習課 |