這一篇是紀錄閱讀官方文件 Add services 的筆記內容。
本篇的內容是接續 Day08 的後續內容。
到目前為止,我們元件中接收資料都是以同步的形式接收資料庫中的資料,但是,在實務上的運用不是用這種形式來接收資料的,而是用非同步的方式去遠端的伺服器端取得所需的資料。
所以,我們會利用 Angular 中的 HttpClient.get 的方式來達成非同步從遠端 server 取得資料的機制。
在 Angular 中的 HttpClient.get() 是會回傳一個 Observable 的結果。
所以,我們要引入一些來自 RxJS 的內容來更改一下原本的 HersoService 檔案的回傳資料型別,然後,改一下原本的 getHeroes 函式的回傳資料型別
--- hero.service.ts ---
import { HERO } from './hero';
import { HEROES } from './mock-heroes';
import { Observable, of } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class HeroService {
// ...
getHeroes(): Observable<HERO[]> {
const heroes = of(HEROES);
return heroes;
}
}
可以看到上面的範例有寫 of(HEROES) ,它會回傳一個 Observable<HERO[]>,它會發送出一個單一值,這個值是一個英雄資料的陣列。
因為,在 HeroService 的回傳資料型別已經改成 Observable<Hero[]>,所以,我們要更改一下 HeroComponent 的 getHeroes 的內容
--- heroes.component.ts ---
getHeroes(): void {
this.heroService.getHeroes()
.subscribe(heroes => this.heroes = heroes);
}
加入 Observable 到我們回傳的資料格式之後,這個回傳的機制就會等待 Observable 發送出含有英雄資料的陣列,接著,才會執行 subscribe 的內容,也就是將發送出來的陣列設給 HeroComponent 的 heroes 中。
接下來我們要新增 MessageComponent 和 MessageService,
MessageComponent 會負責展示訊息在畫面的底部,
MessageService 會負責傳送要被呈現的訊息內容
輸入 ng generate component messages
,新增這個元件之後,再把它加入到 app.component.html。
接著,輸入 ng generate service message
來新增這個服務,然後,在它的檔案內容加入以下的內容,內容就是把 cache 住的訊息內容加入其他內容或者清除這個 cache 的內容。
--- message.service.ts ---
@Injectable({
providedIn: 'root',
})
export class MessageService {
messages: string[] = [];
add(message: string) {
this.messages.push(message);
}
clear() {
this.messages = [];
}
}
接下來,我們把 MessageService 注入到 HeroService 中,
並且將 MessageService 的加入訊息內容的功能,加到 getHeroes 中
--- hero.service.ts ---
import { MessageService } from './message.service';
export class HeroService {
constructor(private messageService:MessageService) { }
getHeroes(): Observable<HERO[]> {
const heroes = of(HEROES);
this.messageService.add('HeroService: fetched heroes'); // 加入MessageService服務
return heroes;
}
}
因為 MessagesComponent 元件是負責將 cache 的訊息呈現出來,所以,也要將 MessageService 的內容引入這個元件中
--- messages.component.ts ---
import { MessageService } from '../message.service';
@Component({
// ...
})
export class MessagesComponent implements OnInit {
constructor(public messageService:MessageService) { }
}
Note:
這邊要注意一下,我們在建構式將 messageService 設為 public 成員,因為,未來它會被樣板中的元素做連結,若用 private 來宣告它的話,就沒有辦法在樣板中調用它。
Angular 只有連結 public 的元件屬性
接下來就把 MessageService 的內容加到 MessagesComponent 的樣板中吧~
--- messages.component.html ---
<div *ngIf="messageService.messages.length">
<h2>Messages</h2>
<button class="clear"
(click)="messageService.clear()">Clear messages</button>
<div *ngFor='let message of messageService.messages'> {{message}} </div>
</div>
這邊可以注意到一點,有特別加了 *ngIf 的判定,來防止當 messageService.messages 沒有任何內容的時候,瀏覽器會報錯。
接下來,我們也要將 messageService 加入到 HeroComponent 中,讓當有英雄被點擊後,被點選的英雄資訊會出現在 messageService 的提示訊息中。
import { MessageService } from '../message.service';
@Component({
// ...
})
export class HeroesComponent implements OnInit {
// ...
constructor(private heroService: HeroService, private messageService: MessageService) { }
onSelect(hero: Hero): void {
this.selectedHero = hero;
this.messageService.add(`HeroesComponent: Selected hero id=${hero.id}`);
}
}
完成以上的內容之後,就可以看到以下的操作動畫成果囉~
這邊做個總結