docs(toh-pt6): bind to a component method rather than a RxJS Subject

updated text accordingly
This commit is contained in:
Ward Bell 2016-07-19 12:26:14 -07:00
parent 72efdc5e5b
commit ce2e14e4ae
3 changed files with 34 additions and 28 deletions

View File

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

View File

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

View File

@ -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.
We're going to access that `Observable` and turn the stream
of strings into a stream of `Hero[]` arrays, the `heroes` property.
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:
+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.