Angular(17) - 从服务端获取数据 - 官方教程英雄指南之添加智能搜索 (详细解说)

1 创建搜索组件 HeroSearchComponent

ng generate component hero-search

2 添加搜索组件模板

  • 需要一个搜索框,用来记录用户输入的值 <input #searchBox id="search-box" (input)="search(searchBox.value)" />
  • 需要一个显示列表,用来呈现搜索结果给用户 <li *ngFor="let hero of heroes$ | async" >
  • 请注意这里是 heroes$ | async, *ngFor只会遍历数据,而heroes$ 是个Observable 而不是数组. 所以这里才会需要| async 管道后面的async,用来自动订阅 Observable
src/app/hero-search/hero-search.component.html
<div id="search-component">
  <h4><label for="search-box">Hero Search</label></h4>

  <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>

3 修改搜索组件类

src/app/hero-search/hero-search.component.ts
import { Component, OnInit } from '@angular/core';

import { Observable, Subject } from 'rxjs';

import {
   debounceTime, distinctUntilChanged, switchMap
 } from 'rxjs/operators';

import { Hero } from '../hero';
import { HeroService } from '../hero.service';

@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) {}

  // Push a search term into the observable stream.
  search(term: string): void {
    this.searchTerms.next(term);
  }

  ngOnInit(): void {
    this.heroes$ = this.searchTerms.pipe(
      // wait 300ms after each keystroke before considering the term
      debounceTime(300),

      // ignore new term if same as previous term
      distinctUntilChanged(),

      // switch to new search observable each time the term changes
      switchMap((term: string) => this.heroService.searchHeroes(term)),
    );
  }
}
  • 3.1 请注意这里 heroes$ 声明为一个 Observable
heroes$: Observable<Hero[]>;
  • 3.2 Subject 既是可观察对象的数据源,本身也是 Observable。 你可以像订阅任何 Observable 一样订阅 Subject。
private searchTerms = new Subject<string>();

// Push a search term into the observable stream.
search(term: string): void {
  this.searchTerms.next(term);
}
  • 3.3 搜索页面中将调用 search方法,每当用户在文本框中输入时,这个事件绑定就会使用文本框的值(搜索词)调用 search() 函数。 searchTerms 变成了一个能发出搜索词的稳定的流。
<input #searchBox id="search-box" (input)="search(searchBox.value)" />

4 搜索的最佳实践 Best Practice

  • 4.1 先上代码
this.heroes$ = this.searchTerms.pipe(
  // wait 300ms after each keystroke before considering the term
  debounceTime(300),

  // ignore new term if same as previous term
  distinctUntilChanged(),

  // switch to new search observable each time the term changes
  switchMap((term: string) => this.heroService.searchHeroes(term)),
);
  • 4.2 如果每当用户击键后就直接调用 searchHeroes() 将导致创建海量的 HTTP 请求,浪费服务器资源并干扰数据调度计划。那该如何做呢?
在传出最终字符串之前,debounceTime(300) 将会等待,直到新增字符串的事件暂停了 300 毫秒。 你实际发起请求的间隔永远不会小于 300ms。
// wait 300ms after each keystroke before considering the term
  debounceTime(300),
  • 4.3 性能优化,如果新的输入term和原来的一样, 则不会发送请求,因为结果也一样
// ignore new term if same as previous term
  distinctUntilChanged(),
  • 4.4 switchMap() 会为每个从 debounce()distinctUntilChanged() 中通过的搜索词调用搜索服务。 它会取消并丢弃以前的搜索可观察对象,只保留最近的。
// switch to new search observable each time the term changes
  switchMap((term: string) => this.heroService.searchHeroes(term)),

5 看下实际效果

result

posted @ 2020-08-20 16:35  三重罗生门  阅读(269)  评论(0编辑  收藏  举报