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 --> | <!-- #docregion --> | ||||||
| <div id="search-component"> | <div id="search-component"> | ||||||
|   <h4>Hero Search</h4> |   <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> | ||||||
|     <div *ngFor="let hero of heroes | async" |     <div *ngFor="let hero of heroes | async" | ||||||
|          (click)="gotoDetail(hero)" class="search-result" > |          (click)="gotoDetail(hero)" class="search-result" > | ||||||
|  | |||||||
| @ -14,22 +14,26 @@ import { Hero } from './hero'; | |||||||
|   providers: [HeroSearchService] |   providers: [HeroSearchService] | ||||||
| }) | }) | ||||||
| export class HeroSearchComponent implements OnInit { | export class HeroSearchComponent implements OnInit { | ||||||
|   // #docregion subject
 |  | ||||||
|   search = new Subject<string>(); |  | ||||||
|   // #enddocregion subject
 |  | ||||||
|   // #docregion search
 |   // #docregion search
 | ||||||
|   heroes: Observable<Hero>; |   heroes: Observable<Hero>; | ||||||
|   // #enddocregion search
 |   // #enddocregion search
 | ||||||
|  |   // #docregion searchSubject
 | ||||||
|  |   searchSubject = new Subject<string>(); | ||||||
|  |   // #enddocregion searchSubject
 | ||||||
| 
 | 
 | ||||||
|   constructor( |   constructor( | ||||||
|     private heroSearchService: HeroSearchService, |     private heroSearchService: HeroSearchService, | ||||||
|     private router: Router) {} |     private router: Router) {} | ||||||
|  |   // #docregion searchSubject
 | ||||||
| 
 | 
 | ||||||
| 
 |   // Push a search term into the observable stream.
 | ||||||
|  |   search(term: string) { this.searchSubject.next(term); } | ||||||
|  |   // #enddocregion searchSubject
 | ||||||
|   // #docregion search
 |   // #docregion search
 | ||||||
|  | 
 | ||||||
|   ngOnInit() { |   ngOnInit() { | ||||||
|     this.heroes = this.search |     this.heroes = this.searchSubject | ||||||
|       .asObservable()           // "cast" as Observable
 |       .asObservable()           // cast as Observable
 | ||||||
|       .debounceTime(300)        // wait for 300ms pause in events
 |       .debounceTime(300)        // wait for 300ms pause in events
 | ||||||
|       .distinctUntilChanged()   // ignore if next search term is same as previous
 |       .distinctUntilChanged()   // ignore if next search term is same as previous
 | ||||||
|       .switchMap(term => term   // switch to new observable each time
 |       .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. |   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') | +makeExample('toh-6/ts/app/hero-search.component.html', null,'hero-search.component.html') | ||||||
| :marked | :marked | ||||||
|   As the user types in the search box, a *keyup* event binding calls `search.next` with the new search box value. |   As the user types in the search box, a *keyup* event binding calls the component's `search` 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. |  | ||||||
| 
 | 
 | ||||||
|   The `*ngFor` repeats *hero* objects from the component's `heroes` property. No surprise there. |   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. |   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 that until we flow it through the `AsyncPipe` (`heroes | async`). |   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`. |   The `AsyncPipe` subscribes to the observable and produces the array of heroes to `*ngFor`. | ||||||
| 
 | 
 | ||||||
|   Time to create the `HeroSearchComponent` class and metadata. |   Time to create the `HeroSearchComponent` class and metadata. | ||||||
| +makeExample('toh-6/ts/app/hero-search.component.ts', null,'hero-search.component.ts') | +makeExample('toh-6/ts/app/hero-search.component.ts', null,'hero-search.component.ts') | ||||||
| :marked | :marked | ||||||
|   Scroll down to where we create the `search` subject. |   Focus on the `searchSubject`. | ||||||
| +makeExample('toh-6/ts/app/hero-search.component.ts', 'subject') | +makeExample('toh-6/ts/app/hero-search.component.ts', 'searchSubject')(format=".") | ||||||
| :marked | :marked | ||||||
|   We're binding to that `search` subject in our template. |   A `Subject` is a producer of an _observable_ event stream.  | ||||||
|   The user is sending it a stream of strings, the filter criteria for the name search. |   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`.  |   A `Subject` is also an `Observable`.  | ||||||
|   We're going to access that `Observable` and append operators to it that turn the stream |   We're going to access that `Observable` and turn the stream | ||||||
|   of strings into a stream of `Hero[]` arrays. |   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=".") | +makeExample('toh-6/ts/app/hero-search.component.ts', 'search')(format=".") | ||||||
| :marked | :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. |   * 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  |   * `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. |   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. |   * `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 | .l-sub-section | ||||||
|   :marked |   :marked | ||||||
| @ -462,10 +460,14 @@ block review | |||||||
|      |      | ||||||
|     `switchMap` preserves the original request order while returning |     `switchMap` preserves the original request order while returning | ||||||
|      only the observable from the most recent http call.  |      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  |     We also short-circuit the http call and return an observable containing an empty array  | ||||||
|     if the search text is empty. |     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 | :marked | ||||||
|   * `catch` intercepts a failed observable.  |   * `catch` intercepts a failed observable.  | ||||||
|   Our simple example prints the error to the console; a real life application should do better. |   Our simple example prints the error to the console; a real life application should do better. | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user