1. 程式人生 > >Angular學習筆記9:服務(Service)(2)

Angular學習筆記9:服務(Service)(2)

可觀察的物件(observable)的資料

在現在的是情況下:HeroService.getHeroes() 的函式簽名是同步的,它所隱含的假設是 HeroService 總是能同步獲取英雄列表資料。 而 HeroesComponent 也同樣假設能同步取到 getHeroes() 的結果。但是在實際的專案中,這種情況幾乎是不可能實現的,因為,在實際的專案中,這些資料是來源於遠端的伺服器上,而這個過程始終是一個非同步的過程。

 在實際的專案中:HeroService 必須等伺服器給出響應, 而 getHeroes()

 不能立即返回英雄資料, 瀏覽器也不會在該服務等待期間停止響應。HeroService.getHeroes() 必須具有某種形式的非同步函式簽名。它可以使用回撥函式,可以返回 Promise(承諾),也可以返回 Observable(可觀察物件)。

一. 重新寫一個可觀察物件版本的 HeroService

Observable簡介:Observable 是 RxJS中的一個關鍵類。

重寫

1.開啟 HeroService 檔案,並從 RxJS 中匯入 Observable 和 of
 符號。

import { Observable, of } from 'rxjs';

2. 修改getHeroes 方法:

 
  1. getHeroes(): Observable<Hero[]> {

  2. return of(HEROES);

  3. }

注意:of(HEROES) 會返回一個 Observable<Hero[]>,它會發出單個值,這個值就是這些模擬英雄的陣列。

3.在 HeroesComponent 中訂閱 

之前的HeroService.getHeroes

 方法返回的是一個 Hero[], 現在它返回的是 Observable<Hero[]>。所以現在必須在 HeroesComponent 中也向本服務中的這種Observable形式變化。

修改getHeroes():

 
  1. getHeroes(): void {

  2. this.heroService.getHeroes()

  3. .subscribe(heroes => this.heroes = heroes);

  4. }

Observable.subscribe() 是關鍵的差異點。

兩個版本的差別:

第一個版本:

hero的陣列賦值給了該元件的 heroes 屬性。 這種賦值是同步的,這裡包含的假設是伺服器能立即返回hero陣列或者瀏覽器能在等待伺服器響應時凍結介面,使其等到後端的資料返回以後,介面在再次解凍。

第二個版本:

應用會等待Observable 發出這個hero陣列,這種請求可能立即發生,也可能會在幾分鐘之後。 然後,subscribe 函式把這個hero陣列傳給這個回撥函式,該函式把hero陣列賦值給元件的 heroes 屬性。使用這種非同步方式,當 HeroService 從遠端伺服器獲取hero資料時,應用就可以工作了。(此過程可以想像類似的想像於將介面暫時凍結,然後等到遠端的伺服器將需要的資料返回以後,自動的將介面又重新的解凍,使應用重新開始工作)

 

二. 顯示資訊

學習目標:

  • 新增一個 MessagesComponent,它在螢幕的底部顯示應用中的訊息。

  • 建立一個可注入的、全應用級別的 MessageService,用於傳送要顯示的訊息。

  • 把 MessageService 注入到 HeroService 中。

  • 當 HeroService 成功獲取了英雄資料時顯示一條訊息。

1.建立MessagesComponent(推薦使用CLI建立)

 
  1. wjydeMacBook-Pro:demo wjy$ ng generate component messages

  2. CREATE src/app/messages/messages.component.css (0 bytes)

  3. CREATE src/app/messages/messages.component.html (27 bytes)

  4. CREATE src/app/messages/messages.component.spec.ts (642 bytes)

  5. CREATE src/app/messages/messages.component.ts (277 bytes)

  6. UPDATE src/app/app.module.ts (638 bytes)

CLI 在 src/app/messages中建立了元件檔案,並且把 MessagesComponent 宣告在了 AppModule 中。

2.修改 AppComponent 的模板來顯示所生成的 MessagesComponent

 
  1. <h1>{{title}}</h1>

  2. <app-heroes></app-heroes>

  3. <app-messages></app-messages>

<app-messages> 是MessagesComponent的  selector: 'app-messages'

3.建立 MessageService

 
  1. wjydeMacBook-Pro:demo wjy$ ng generate service messages

  2. CREATE src/app/messages.service.spec.ts (386 bytes)

  3. CREATE src/app/messages.service.ts (137 bytes)

4.在MessagesService中增加一個message的變數,並且在寫一個新增message的方法和清空message的方法。

 
  1. import {Injectable} from '@angular/core';

  2.  
  3. @Injectable({

  4. providedIn: 'root'

  5. })

  6. export class MessageService {

  7.  
  8. // messages 變數

  9. messages: string[] = [];

  10.  
  11. // 增加變數的函式

  12. add(message: string) {

  13. this.messages.push(message);

  14. }

  15.  
  16. // 清空變數的函式

  17. clear() {

  18. this.messages = [];

  19. }

  20.  
  21. // 構造方法

  22. constructor() {

  23. }

  24. }

5.把它注入到 HeroService 中

開啟 HeroService,將匯入 MessageService

import { MessageService } from './message.service';

6.修改這個建構函式

新增一個私有的 messageService 屬性引數。 Angular 將會在建立 HeroService 時把 MessageService 的單例注入到這個屬性中。

constructor(private messageService: MessageService) { }

7.從 HeroService 中傳送一條訊息

修改 getHeroes 方法,在獲取到英雄陣列時傳送一條訊息。

 
  1. getHeroes(): Observable<Hero[]> {

  2. this.messageService.add('HeroService: fetched heroes');

  3. return of(HEROES);

  4. }

8.從 HeroService 中顯示訊息

開啟 MessagesComponent,並且匯入 MessageService

import { MessageService } from '../message.service';

修改MessagesComponent建構函式,新增一個 public 的 messageService 屬性。 Angular 將會在建立 MessagesComponent 的例項時 把 MessageService 的例項注入到這個屬性中。

此處設messageService為public是因為要在messageService在模版檔案中用到。

注意:⚠️Angular只會繫結公共的屬性

9.繫結到 MessageService

修改messageComponent的模版檔案

 
  1. <div *ngIf="messageService.messages.length">

  2. <h2>Messages</h2>

  3. <button class="clear" (click)="messageService.clear()">clear

  4. </button>

  5. <div *ngFor='let message of messageService.messages'> {{message}}</div>

  6. </div>

  • *ngIf 只有在有訊息時才會顯示訊息區。

  • *ngFor用來在一系列 <div> 元素中展示訊息列表。

  • Angular 的事件繫結把按鈕的 click 事件繫結到了 MessageService.clear()這個方法上了

10.儲存執行,重新整理瀏覽器,介面如下

11.使Message更好看點

為MessagesComponent元件新增私有樣式

 
  1. h2 {

  2. color: red;

  3. font-family: Arial, Helvetica, sans-serif;

  4. font-weight: lighter;

  5. }

  6. body {

  7. margin: 2em;

  8. }

  9. body, input[text], button {

  10. color: crimson;

  11. font-family: Cambria, Georgia;

  12. }

  13.  
  14. button.clear {

  15. font-family: Arial;

  16. background-color: #eee;

  17. border: none;

  18. padding: 5px 10px;

  19. border-radius: 4px;

  20. cursor: pointer;

  21. cursor: hand;

  22. }

  23. button:hover {

  24. background-color: #cfd8dc;

  25. }

  26. button:disabled {

  27. background-color: #eee;

  28. color: #aaa;

  29. cursor: auto;

  30. }

  31. button.clear {

  32. color: #888;

  33. margin-bottom: 12px;

  34. }