Skip to content
This repository was archived by the owner on Dec 4, 2017. It is now read-only.

Commit a44412d

Browse files
committed
docs(toh-pt6): add observables - ward's changes
1 parent 7dbbc0c commit a44412d

File tree

5 files changed

+93
-48
lines changed

5 files changed

+93
-48
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
<!-- #docregion -->
22
<div id="search-component">
3-
<h4>Hero Search</h4>
4-
<input #searchBox id="search-box" (keyup)="search.next(searchBox.value)" />
3+
<h4>Hero Search</h4>
4+
<input #searchBox id="search-box" (keyup)="search.next(searchBox.value)" />
55
<div>
6-
<div class="search-result" (click)="gotoDetail(hero)" *ngFor="let hero of heroes">
6+
<div *ngFor="let hero of heroes | async"
7+
(click)="gotoDetail(hero)" class="search-result" >
78
{{hero.name}}
89
</div>
910
</div>
10-
</div>
11+
</div>

public/docs/_examples/toh-6/ts/app/hero-search.component.ts

+25-13
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,37 @@ import { Hero } from './hero';
1313
providers: [HeroSearchService]
1414
})
1515
export class HeroSearchComponent implements OnInit {
16+
// #docregion subject
1617
search = new Subject<string>();
17-
heroes: Hero[] = [];
18+
// #enddocregion subject
1819

19-
constructor(private _heroSearchService: HeroSearchService, private _router: Router) {}
20+
heroes: Observable<Hero>;
21+
22+
constructor(
23+
private heroSearchService: HeroSearchService,
24+
private router: Router) {}
2025

21-
gotoDetail(hero: Hero) {
22-
let link = ['/detail', hero.id];
23-
this._router.navigate(link);
24-
}
2526

2627
// #docregion search
2728
ngOnInit() {
28-
this.search.asObservable()
29-
.debounceTime(300)
30-
.distinctUntilChanged()
31-
.switchMap(term => term ? this._heroSearchService.search(term)
32-
: Observable.of([]))
33-
.subscribe(result => this.heroes = result,
34-
error => console.log(error));
29+
this.heroes = this.search
30+
.asObservable()
31+
.debounceTime(300)
32+
.distinctUntilChanged()
33+
34+
.switchMap(term => term
35+
? this.heroSearchService.search(term)
36+
: Observable.of<Hero[]>([]))
37+
38+
.catch(error => {
39+
console.log(error);
40+
return Observable.throw(error);
41+
});
3542
}
3643
// #enddocregion search
44+
45+
gotoDetail(hero: Hero) {
46+
let link = ['/detail', hero.id];
47+
this.router.navigate(link);
48+
}
3749
}

public/docs/_examples/toh-6/ts/app/hero-search.service.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,18 @@
22
import { Injectable } from '@angular/core';
33
import { Http, Response } from '@angular/http';
44

5+
import { Hero } from './hero';
6+
57
@Injectable()
68
export class HeroSearchService {
79

8-
constructor(private _http: Http) {}
10+
constructor(private http: Http) {}
911

1012
// #docregion observable-search
1113
search(term: string) {
12-
return this._http
14+
return this.http
1315
.get(`app/heroes/?name=${term}+`)
14-
.map((r: Response) => r.json().data);
16+
.map((r: Response) => r.json().data as Hero[]);
1517
}
1618
// #enddocregion observable-search
1719
}

public/docs/_examples/toh-6/ts/app/rxjs-operators.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
// #docregion
2+
import 'rxjs/add/observable/of';
3+
4+
import 'rxjs/add/operator/catch';
5+
import 'rxjs/add/operator/debounceTime';
6+
import 'rxjs/add/operator/distinctUntilChanged';
7+
import 'rxjs/add/operator/do';
8+
import 'rxjs/add/operator/filter';
29
import 'rxjs/add/operator/map';
310
import 'rxjs/add/operator/switchMap';
4-
import 'rxjs/add/operator/filter';
5-
import 'rxjs/add/operator/do';
6-
import 'rxjs/add/operator/distinctUntilChanged';
7-
import 'rxjs/add/operator/debounceTime';
8-
import 'rxjs/add/observable/of';

public/docs/ts/latest/tutorial/toh-pt6.jade

+51-23
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ block get-heroes-details
131131
:marked
132132
The Angular `http.get` returns an RxJS `Observable`.
133133
*Observables* are a powerful way to manage asynchronous data flows.
134-
We'll learn about `Observables` *later*.
134+
We'll learn about [Observables](#observables) later in this chapter.
135135

136136
For *now* we get back on familiar ground by immediately converting that `Observable` to a `Promise` using the `toPromise` operator.
137137
+makeExample('toh-6/ts/app/hero.service.ts', 'to-promise')(format=".")
@@ -361,41 +361,69 @@ block review
361361
:marked
362362
## Observables
363363

364-
In this section we discuss `Observables` as an alternative to promises when processing http calls.
364+
Each `Http` method returns an `Observable` of HTTP `Response` objects.
365365

366-
`Http` calls return RxJS observables by default, but so far we've been hiding that by converting the observable to a promise by calling `toPromise`.
366+
Our `HeroService` converts that `Observable` into a `Promise` and returns the promise to the caller.
367+
In this section we learn to return the `Observable` directly and discuss when and why that might be
368+
a good thing to do.
367369

368-
Observables and promises are both great for processing http calls, but as we will see, the observables api is much richer.
370+
### Background
371+
An *observable* is a stream of events that we can process with array-like operators.
369372

370-
`RxJs` offers a wide variety of operators that we can use to manage event flows.
373+
Angular core has basic support for observables. We developers augment that support with
374+
operators and extensions from the [RxJS Observables](http://reactivex.io/rxjs/) library.
375+
We'll see how shortly.
371376

372-
In this section we will discuss how to use some of these operators to build a type-to-search filter where we can search for heroes by name.
377+
Recall that our `HeroService` quickly chained the `toPromise` operator to the `Observable` result of `http.get`.
378+
That operator converted the `Observable` into a `Promise` and we passed that promise back to the caller.
379+
380+
Converting to a promise is often a good choice. We typically ask `http` to fetch a single chunk of data.
381+
When we receive the data, we're done.
382+
A single result in the form of a promise is easy for the calling component to consume
383+
and it helps that promises are widely understood by JavaScript programmers.
384+
385+
But requests aren't always "one and done". We may start one request,
386+
then cancel it, and make a different request ... before the server has responded to the first request.
387+
Such a _request-cancel-new-request_ sequence is difficult to implement with *promises*.
388+
It's easy with *observables* as we'll see.
389+
390+
### Search-by-name example
391+
We're going to add a *hero search* feature to the Tour of Heroes.
392+
As the user types a name into a search box, we'll make repeated http requests for heroes filtered by that name.
373393

374-
We start by creating `HeroSearchService`, a simple service for sending search queries to our api.
394+
We start by creating `HeroSearchService` that sends search queries to our server's web api.
375395

376396
+makeExample('toh-6/ts/app/hero-search.service.ts', null, 'app/hero-search.service.ts')(format=".")
377397

378398
:marked
379-
The http call in `HeroSearchService` is not that different from our previous http calls, but we no longer call `toPromise`. This means we will return an observable instead of a promise.
399+
The `http.get` call in `HeroSearchService` is similar to the `http.get` call in the `HeroService`.
400+
The notable difference: we no longer call `toPromise`.
401+
We simply return the *observable* instead.
380402

381-
Now, let's implement our search component `HeroSearchComponent`.
382-
383-
+makeTabs(
384-
`toh-6/ts/app/hero-search.component.ts,
385-
toh-6/ts/app/hero-search.component.html`,
386-
null,
387-
`hero-search.component.ts,
388-
hero-search.component.html`
389-
)
403+
### HeroSearchComponent
404+
Let's create a new `HeroSearchComponent` that calls this new `HeroSearchService`.
390405

406+
The component template is simple - just a textbox and a list of matching search results.
407+
+makeExample('toh-6/ts/app/hero-search.component.html', null,'hero-search.component.html')
391408
:marked
392-
The `HeroSearchComponent` UI is simple - just a textbox and a list of matching search results.
393-
394-
To keep track of text changes in the search box we are using an RxJs `Subject`.
409+
As the user types in the search box, a *keyup* event binding calls `search.next` with the new search box value.
395410

396-
Observables are great for managing event streams. In our example we will actually be dealing with two different types of event streams:
411+
The componen'ts data bound `search` property returns a `Subject`.
412+
A `Subject` is a producer of an _observable_ event stream.
413+
Each call to `search.next` puts a new string into this subject's _observable_ stream.
414+
415+
Create a `HeroSearchComponent` as follows.
416+
+makeExample('toh-6/ts/app/hero-search.component.ts', null,'hero-search.component.ts')
417+
:marked
418+
Scroll down to where we create the `search` subject.
419+
+makeExample('toh-6/ts/app/hero-search.component.ts', 'subject')
420+
:marked
421+
We're binding to that `search` subject in our template.
422+
The user is sending it a stream of strings, the filter criteria for the name search.
397423

398-
1) Key events from typing into the search textbox
424+
A `Subject` is also an `Observable`.
425+
We're going to access that `Observable` and add operators to that turn the stream
426+
of filter strings into a stream of http search results.
399427

400428
2) Results from http calls based on values entered in the search textbox
401429

@@ -539,4 +567,4 @@ block file-summary
539567
hero-search.component.ts,
540568
hero-search.service.html,
541569
rxjs-operators.ts`
542-
)
570+
)

0 commit comments

Comments
 (0)