Angular6学习笔记13:HTTP(3)

本文介绍如何在Angular项目中实现搜索功能,包括创建搜索方法、添加搜索UI组件、使用RxJS操作符优化搜索请求,以及如何处理HTTP请求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

HTTP

继学习笔记12以后,可以模拟向后端发送get/post/put/delete请求了。在项目中,有一个table,这个table的数据非常多,就好比现在的heroList,需要根据用户输入的信息发送给远端服务器,让远端服务器通过这个信息,返回搜索结果。现在要检索heroList中的信息,就需要一个输入框,让用户输入检索的值,然后将这个值发送给远端服务器(模拟),然后让远端服务器(模拟)返回检索的结果。

1.在heroService中创建一个关于搜索的方法:searchHeroes()

searchHeroes(term: string): Observable<Hero[]> {
  if (!term.trim()) {
    return of([]);
  }
  return this.http.get<Hero[]>(`${this.heroesUrl}/?name=${term}`).pipe(
    tap(_ => this.log(`found heroes matching "${term}"`)),
    catchError(this.handleError<Hero[]>('searchHeroes', []))
  );
}

在这个方法中,当没有搜素词,则返回一个空的数组,当有搜索词的时候,在url中拼接上name

2.在仪表盘的组件中添加搜索功能

<h3>Top Heroes</h3>
<div class="grid grid-pad">
  <a *ngFor="let hero of heroes" class="col-1-4"
      routerLink="/detail/{{hero.id}}">
    <div class="module hero">
      <h4>{{hero.name}}</h4>
    </div>
  </a>
</div>

<app-hero-search></app-hero-search>

这里会让这个应用挂了,因为找不到<app-hero-search></app-hero-search>(接下来创建)

3.创建HeroSearchComponent

利用angular CLI 创建组件

ng generate component hero-search

CLI 生成了 HeroSearchComponent 的三个文件,并把该组件添加到了 AppModule 的声明中。

修改HeroSearch组件的模版文件:

<div id="search-component">
  <h4>Hero Search</h4>
  <input #searchBox id="search-box" (keyup)="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>

在模版文件中,创建了keyup 事件,这个keyup事件绑定会调用该组件的 search() 方法,并传入新的搜索框的值。

*ngFor 是在一个名叫 heroes$ 的列表上迭代,在这里$ 是一个命名惯例,用来表明 heroes$ 是一个 Observable,而不是数组。

正常情况下,*ngFor是不能直接使用Observable,此时,就要用到管道,利用管道字符(|),后面紧跟着一个 async,它表示 Angular 的 AsyncPipeAsyncPipe 会自动订阅到 Observable,这样你就不用再在组件类中订阅了。

美化这个HeroSearch组件,修改heroSearch的CSS文件

.search-result li {
  border-bottom: 1px solid gray;
  border-left: 1px solid gray;
  border-right: 1px solid gray;
  width:195px;
  height: 16px;
  padding: 5px;
  background-color: white;
  cursor: pointer;
  list-style-type: none;
}

.search-result li:hover {
  background-color: #607D8B;
}

.search-result li a {
  color: #888;
  display: block;
  text-decoration: none;
}

.search-result li a:hover {
  color: white;
}
.search-result li a:active {
  color: white;
}
#search-box {
  width: 200px;
  height: 20px;
}


ul.search-result {
  margin-top: 0;
  padding-left: 0;
}

修改HeroSearch 的类文件

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)),
    );
  }
}

注意,heroes$ 声明为一个 Observable

a.RxJS Subject 类型的 searchTerms

Subject 既是可观察对象的数据源,本身也是 Observable。可以像订阅任何 Observable 一样订阅 Subject。还可以通过调用它的 next(value) 方法往 Observable 中推送一些值,就像 search() 方法中一样。search() 是通过对文本框的 keystroke 事件的事件绑定来调用的。

private searchTerms = new Subject<string>();

search(term: string): void {
  this.searchTerms.next(term);
}

每当用户在文本框中输入时,这个事件绑定就会使用文本框的值(搜索词)调用 search() 函数。 searchTerms 变成了一个能发出搜索词的稳定的流。

b.串联 RxJS 操作符

每当用户击键后就直接调用 searchHeroes() 将导致创建海量的 HTTP 请求,浪费服务器资源并消耗大量网络流量。

应该怎么做呢?ngOnInit() 往 searchTerms 这个可观察对象的处理管道中加入了一系列 RxJS 操作符,用以缩减对 searchHeroes() 的调用次数,并最终返回一个可及时给出英雄搜索结果的可观察对象(每次都是 Hero[] )。

this.heroes$ = this.searchTerms.pipe(
  debounceTime(300),
  distinctUntilChanged(),
  switchMap((term: string) => this.heroService.searchHeroes(term)),
);
  • 在传出最终字符串之前,debounceTime(300) 将会等待,直到新增字符串的事件暂停了 300 毫秒。 你实际发起请求的间隔永远不会小于 300ms。

  • distinctUntilChanged() 会确保只在过滤条件变化时才发送请求。

  • switchMap() 会为每个从 debounce 和 distinctUntilChanged 中通过的搜索词调用搜索服务。 它会取消并丢弃以前的搜索可观察对象,只保留最近的。

借助 switchMap 操作符, 每个有效的击键事件都会触发一次 HttpClient.get() 方法调用。 即使在每个请求之间都有至少 300ms 的间隔,仍然可能会同时存在多个尚未返回的 HTTP 请求。

switchMap() 会记住原始的请求顺序,只会返回最近一次 HTTP 方法调用的结果。 以前的那些请求都会被取消和舍弃。

注意,取消前一个 searchHeroes() 可观察对象并不会中止尚未完成的 HTTP 请求。 那些不想要的结果只会在它们抵达应用代码之前被舍弃。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wujiayucn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值