docs(toh-pt6): bind to a component method rather than a RxJS Subject
updated text accordingly
This commit is contained in:
parent
72efdc5e5b
commit
ce2e14e4ae
|
@ -1,7 +1,7 @@
|
|||
<!-- #docregion -->
|
||||
<div id="search-component">
|
||||
<h4>Hero Search</h4>
|
||||
<input #searchBox id="search-box" (keyup)="search.next(searchBox.value)" />
|
||||
<input #searchBox id="search-box" (keyup)="search(searchBox.value)" />
|
||||
<div>
|
||||
<div *ngFor="let hero of heroes | async"
|
||||
(click)="gotoDetail(hero)" class="search-result" >
|
||||
|
|
|
@ -14,22 +14,26 @@ import { Hero } from './hero';
|
|||
providers: [HeroSearchService]
|
||||
})
|
||||
export class HeroSearchComponent implements OnInit {
|
||||
// #docregion subject
|
||||
search = new Subject<string>();
|
||||
// #enddocregion subject
|
||||
// #docregion search
|
||||
heroes: Observable<Hero>;
|
||||
// #enddocregion search
|
||||
// #docregion searchSubject
|
||||
searchSubject = new Subject<string>();
|
||||
// #enddocregion searchSubject
|
||||
|
||||
constructor(
|
||||
private heroSearchService: HeroSearchService,
|
||||
private router: Router) {}
|
||||
// #docregion searchSubject
|
||||
|
||||
|
||||
// Push a search term into the observable stream.
|
||||
search(term: string) { this.searchSubject.next(term); }
|
||||
// #enddocregion searchSubject
|
||||
// #docregion search
|
||||
|
||||
ngOnInit() {
|
||||
this.heroes = this.search
|
||||
.asObservable() // "cast" as Observable
|
||||
this.heroes = this.searchSubject
|
||||
.asObservable() // cast as Observable
|
||||
.debounceTime(300) // wait for 300ms pause in events
|
||||
.distinctUntilChanged() // ignore if next search term is same as previous
|
||||
.switchMap(term => term // switch to new observable each time
|
||||
|
|
|
@ -407,39 +407,37 @@ block review
|
|||
The component template is simple - just a textbox and a list of matching search results.
|
||||
+makeExample('toh-6/ts/app/hero-search.component.html', null,'hero-search.component.html')
|
||||
:marked
|
||||
As the user types in the search box, a *keyup* event binding calls `search.next` with the new search box value.
|
||||
|
||||
The component's data bound `search` property returns a `Subject`.
|
||||
A `Subject` is a producer of an _observable_ event stream.
|
||||
Each call to `search.next` puts a new string into this subject's _observable_ stream.
|
||||
As the user types in the search box, a *keyup* event binding calls the component's `search` with the new search box value.
|
||||
|
||||
The `*ngFor` repeats *hero* objects from the component's `heroes` property. No surprise there.
|
||||
|
||||
But `heroes` is an `Observable` of heroes, not an array of heroes.
|
||||
The `*ngFor` can't do anything with that until we flow it through the `AsyncPipe` (`heroes | async`).
|
||||
But, as we'll soon see, the `heroes` property returns an `Observable` of heroes, not an array of heroes.
|
||||
The `*ngFor` can't do anything with an observable until we flow it through the `AsyncPipe` (`heroes | async`).
|
||||
The `AsyncPipe` subscribes to the observable and produces the array of heroes to `*ngFor`.
|
||||
|
||||
Time to create the `HeroSearchComponent` class and metadata.
|
||||
+makeExample('toh-6/ts/app/hero-search.component.ts', null,'hero-search.component.ts')
|
||||
:marked
|
||||
Scroll down to where we create the `search` subject.
|
||||
+makeExample('toh-6/ts/app/hero-search.component.ts', 'subject')
|
||||
Focus on the `searchSubject`.
|
||||
+makeExample('toh-6/ts/app/hero-search.component.ts', 'searchSubject')(format=".")
|
||||
:marked
|
||||
We're binding to that `search` subject in our template.
|
||||
The user is sending it a stream of strings, the filter criteria for the name search.
|
||||
A `Subject` is a producer of an _observable_ event stream.
|
||||
This `searchSubject` produces an `Observable` of strings, the filter criteria for the name search.
|
||||
|
||||
Each call to `search` puts a new string into this subject's _observable_ stream by calling `next`.
|
||||
|
||||
A `Subject` is also an `Observable`.
|
||||
We're going to access that `Observable` and append operators to it that turn the stream
|
||||
of strings into a stream of `Hero[]` arrays.
|
||||
|
||||
Each user keystroke could result in a new http request returning a new Observable array of heroes.
|
||||
|
||||
This could be a very chatty, taxing our server resources and burning up our cellular network data plan.
|
||||
Fortunately we can chain `Observable` operators to reduce the request flow
|
||||
and still get timely results. Here's how:
|
||||
We're going to access that `Observable` and turn the stream
|
||||
of strings into a stream of `Hero[]` arrays, the `heroes` property.
|
||||
|
||||
+makeExample('toh-6/ts/app/hero-search.component.ts', 'search')(format=".")
|
||||
:marked
|
||||
If we passed every user keystroke directly to the `HeroSearchService`, we'd unleash a storm of http requests.
|
||||
Bad idea. We don't want to tax our server resources and burn through our cellular network data plan.
|
||||
|
||||
Fortunately we can chain `Observable` operators to the string `Observable` that reduce the request flow.
|
||||
We'll make fewer calls to the `HeroSearchService` and still get timely results. Here's how:
|
||||
|
||||
* The `asObservable` operator casts the `Subject` as an `Observable` of filter strings.
|
||||
|
||||
* `debounceTime(300)` waits until the flow of new string events pauses for 300 milliseconds
|
||||
|
@ -449,7 +447,7 @@ block review
|
|||
There's no point in repeating a request for the same search term.
|
||||
|
||||
* `switchMap` calls our search service for each search term that makes it through the `debounce` and `distinctUntilChanged` gauntlet.
|
||||
It discards previous search observables, returning only the latest search service observable.
|
||||
It cancels and discards previous search observables, returning only the latest search service observable.
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
|
@ -462,10 +460,14 @@ block review
|
|||
|
||||
`switchMap` preserves the original request order while returning
|
||||
only the observable from the most recent http call.
|
||||
Results from prior calls will be discarded.
|
||||
Results from prior calls are canceled and discarded.
|
||||
|
||||
We also short-circuit the http call and return an observable containing an empty array
|
||||
if the search text is empty.
|
||||
|
||||
Note that _canceling_ the `HeroSearchService` observable won't actually abort a pending http request
|
||||
until the service supports that feature, a topic for another day.
|
||||
We are content for now to discard unwanted results.
|
||||
:marked
|
||||
* `catch` intercepts a failed observable.
|
||||
Our simple example prints the error to the console; a real life application should do better.
|
||||
|
|
Loading…
Reference in New Issue