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 的 AsyncPipe,AsyncPipe 会自动订阅到 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 请求。 那些不想要的结果只会在它们抵达应用代码之前被舍弃。
本文介绍如何在Angular项目中实现搜索功能,包括创建搜索方法、添加搜索UI组件、使用RxJS操作符优化搜索请求,以及美化搜索组件样式。
4877

被折叠的 条评论
为什么被折叠?



