本篇內容為閱讀官方文件 Get data from a server 的筆記內容。
接續 Day11 的內容。
這個小節為在某個搜尋欄位輸入欲搜尋內容後,程式會將我們輸入的內容打出 HTTP 並夾帶給伺服器端,最後,將相關的搜尋內容送回來給我們,並呈現在畫面上。
step 1.
先在 hero.service.ts 檔案中,定義一個 http.get
去向伺服器要資料的 HTTP 操作。
--- hero.service.ts ---
searchHeroes(term: string): Observable<HERO[]> {
if (!term.trim()) {
return of([]);
}
return this.http.get<HERO[]>(`${this.heroesUrl}/?name=${term}`).pipe(
catchError(this.handleError<HERO[]>('searchHeroes', []))
);
}
以上的內容,就是先去防止輸入的查詢內容是空白的,最後,把查詢內容丟到 http.get
中去查詢,查詢成功後,就會回傳以 Hero 作為資料型別的 Observable 的陣列。
step 2.
接著,新增 hero-search 元件到專案中。
所以,輸入指令 ng g c hero-search ,來創出 HeroSearch 元件。
要特別注意的地方是,官方文件中提供的有關 hero-search 元件的 html 的內容,有下列這一段
<div id="search-component">
<label for="search-box">Hero Search</label>
<input #searchBox id="search-box" (input)="search(searchBox.value)" />
<ul class="search-result">
<li *ngFor="let hero of heroes$ | async" >
<a routerLink="/detail/{{hero.id}}">
{{hero.name}}
</a>
</li>
</ul>
</div>
以上這個 ul 元素其實是一個 dropdown menu,將搜尋的結果呈現在這個 menu 上。
在 li 中,利用 *ngFor
來遍歷的元素是 heroes$
,在後面加一個錢字號 $
,是要特別聲明它是一個 Observable 的實例,不是一個單純的陣列。
另外,我們在 heroes$ 後面接了一個 async,它其實叫做 asyncPipe,它是專門用來監聽一個 Observable 實例是否有變化,並透過 subsrcibe 將該 Observable 實例的最新值傳遞出來,也因為它將 Observable 的值傳遞出來,讓 *ngFor
可以去遍歷它傳遞出來的內容。
接著,我們就要將尋找的相關英雄內容的功能加到 hero-search 元件中了。
import { Component, OnInit } from '@angular/core';
import { HERO } from '../hero'
import { HeroService } from '../hero.service'
import { Subject, Observable } from 'rxjs'
import {
debounceTime, distinctUntilChanged, switchMap
} from 'rxjs/operators';
@Component({
selector: 'app-hero-search',
templateUrl: './hero-search.component.html',
styleUrls: ['./hero-search.component.css']
})
export class HeroSearchComponent implements OnInit {
heroes$!: Observable<HERO[]>;
private searchTerms = new Subject<string>();
constructor(private heroService:HeroService) {
}
search(term: string): void {
this.searchTerms.next(term);
}
ngOnInit(): void {
this.heroes$ = this.searchTerms.pipe(
debounceTime(300),
distinctUntilChanged(),
switchMap((term: string) => this.heroService.searchHeroes(term)),
);
}
}
可以看到 searchTerms 這個變數是 Subject 的實例。
當觸發 seatrch 函式的時候,會傳入搜尋的英雄名稱,接著,在使用 Subject 的 next 方法來呼叫它,並把英雄名稱傳入,接著,就會進到我們在 ngOnInit 定義的 this.searchTerms.pipe
後面一連串的內容,而在 switchMap 這個 operator 的參數 term 就是我們傳入的英雄搜尋名稱,最後,再把它丟到 searchHeroes 裡面。最終,回傳的資料就會存到 this.heroes$ 裡面囉。
在以上的程式碼中引入了好幾個來自 rxjs/operators 的模組,分別是 debounceTime
, distinctUntilChanged
和 switchMap
,會引入它們的原因是因為,要防止當使用者輸入搜尋內容的時候,會連續觸發 searchHeroes 的事件的狀況產生。debounceTime(300)
: 當使用者超過 300 ms 不再輸入之後,就會通過 debounceTime(300)
這關了
distinctUntilChanged
: 若舊值和新值不同的時候,才會通過 distinctUntilChanged
這關
switchMap
: 它會拋棄之前舊的回傳值,並接收由最新發出的 HTTP 請求所回傳的最新的回傳值。
最後,我們將 heroSearch 元件加到 dashBoard 裡面。
<h2>Top Heroes</h2>
<div class="heroes-menu">
<a *ngFor="let hero of heroes" (click)="goDetail(hero.id)">
{{ hero.name }}
</a>
</div>
<app-hero-search></app-hero-search>
這邊做個總結
*ngFor
可以遍歷它。