新新新手阅读 Angular 文件 - Get data from a server(3) - Day12

学习目标

本篇内容为阅读官方文件 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 的实例,不是一个单纯的阵列。

asyncPipe

另外,我们在 heroes$ 後面接了一个 async,它其实叫做 asyncPipe,它是专门用来监听一个 Observable 实例是否有变化,并透过 subsrcibe 将该 Observable 实例的最新值传递出来,也因为它将 Observable 的值传递出来,让 *ngFor 可以去遍历它传递出来的内容。

HeroSearch 元件的内容

接着,我们就要将寻找的相关英雄内容的功能加到 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

在以上的程序码中引入了好几个来自 rxjs/operators 的模组,分别是 debounceTime, distinctUntilChangedswitchMap,会引入它们的原因是因为,要防止当使用者输入搜寻内容的时候,会连续触发 searchHeroes 的事件的状况产生。
debounceTime(300): 当使用者超过 300 ms 不再输入之後,就会通过 debounceTime(300) 这关了

distinctUntilChanged : 若旧值和新值不同的时候,才会通过 distinctUntilChanged 这关

switchMap : 它会抛弃之前旧的回传值,并接收由最新发出的 HTTP 请求所回传的最新的回传值。

加到 dashboard 中

最後,我们将 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>

最终成果

Summary

这边做个总结

  1. 如何使用 asyncPipe 来将 Observable 型别的资料内容传送出来,让 *ngFor 可以遍历它。
  2. 如何搭配使用 subject 并搭配 debounceTime, switchMap,来完成一个 auto-complete 的功能。

<<:  标签图片的方法与实作 - Day 12

>>:  【第十二天 - 递回介绍】

食谱搜寻系统开赛罗~~

主题 :从零开始食谱搜寻系统 Day1 (开赛) 开赛宣言 在开赛第一天,icebear希望自己可以...

Day 30 五个自动化测试的好处

该文章同步发布於:我的部落格 到了铁人赛的最後一天,看了这麽多东西,我们可以来谈谈学这些测试的好处...

[Day 09] 剩下的时间规划

  由於感觉我这前几天的文章都字数以及内容都蛮少的,简单来说就只是想低空 飞过,打混过关;结果想透过...

Day 06 Python 的特点

经过了前面几天的基本教学,相信大家都对 Python 有了基本的认识,也应该有点累了,所以今天来讲一...

[Day28] 平常用不到但又常常需要的缩网址+QRcode产生器

写在前面 除了 Day 01 的 Tobymini 页面管理,可以和同事之间互相分享网页之外, 有时...