docs(http): from Observable.create to Subject, per Rob W

This commit is contained in:
Ward Bell 2016-02-03 22:28:39 -08:00
parent 00d06ca50a
commit 16704e9063
2 changed files with 67 additions and 60 deletions

View File

@ -2,9 +2,9 @@
import {Component} from 'angular2/core'; import {Component} from 'angular2/core';
import {JSONP_PROVIDERS} from 'angular2/http'; import {JSONP_PROVIDERS} from 'angular2/http';
import {Observable} from 'rxjs/Observable'; import {Observable} from 'rxjs/Observable';
// #docregion import-observer // #docregion import-subject
import {Observer} from 'rxjs/Observer'; import {Subject} from 'rxjs/Subject';
// #enddocregion import-observer // #enddocregion import-subject
import {WikipediaService} from './wikipedia.service'; import {WikipediaService} from './wikipedia.service';
@ -26,16 +26,11 @@ export class WikiSmartComponent {
constructor (private _wikipediaService: WikipediaService) { } constructor (private _wikipediaService: WikipediaService) { }
search: (value: string) => void; // #docregion subject
private _searchTermStream = new Subject<string>();
// #docregion observable-create search(term:string) { this._searchTermStream.next(term); }
private _searchTermStream: Observable<string> = // #enddocregion subject
Observable.create(
// #docregion subscribe-fn
(observer:Observer<string>) => this.search = (term) => observer.next(term)
// #enddocregion subscribe-fn
);
// #enddocregion observable-create
// #docregion observable-operators // #docregion observable-operators
items:Observable<string[]> = this._searchTermStream items:Observable<string[]> = this._searchTermStream

View File

@ -1,12 +1,24 @@
include ../../../../_includes/_util-fns include ../../../../_includes/_util-fns
:marked :marked
Most applications read from or write to a remote server via HTTP or JSONP. [HTTP](https://tools.ietf.org/html/rfc2616) is the primary protocol for browser/server communication.
The Angular 2 http client supports both as we learn in this chapter covering: .l-sub-section
:marked
The [`WebSocket`](https://tools.ietf.org/html/rfc6455) protocol is another important communication technology;
we won't cover it in this chapter.
:marked
Modern browsers support two HTTP-based APIs:
[XMLHttpRequest (XHR)](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) and
[JSONP](https://en.wikipedia.org/wiki/JSONP). A few browsers also support
[Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API).
The Angular HTTP client library simplifies application programming of the **XHR** and **JSONP** APIs
as we'll learn in this chapter covering:
[Http client sample overview](#http-client)<br> [Http client sample overview](#http-client)<br>
[Fetch data with http.get](#fetch-data)<br> [Fetch data with http.get](#fetch-data)<br>
[Enable RxJS Operators](#enable-rxjs-operators)<br> [RxJS Observable of HTTP Responses](#rxjs)<br>
[Enabling RxJS Operators](#enable-rxjs-operators)<br>
[Extract JSON data with RxJS map](#map)<br> [Extract JSON data with RxJS map](#map)<br>
[Error handling](#error-handling)<br> [Error handling](#error-handling)<br>
[Log results to console](#do)<br> [Log results to console](#do)<br>
@ -18,26 +30,18 @@ include ../../../../_includes/_util-fns
[Debounce search term input](#more-observables)<br> [Debounce search term input](#more-observables)<br>
[Appendix: the in-memory web api service](#in-mem-web-api)<br> [Appendix: the in-memory web api service](#in-mem-web-api)<br>
We illustrate these topics with code that you can
[run live in a browser](/resources/live-examples/server-communication/ts/plnkr.html).
Try the [live example](/resources/live-examples/server-communication/ts/plnkr.html).
.l-main-section .l-main-section
:marked :marked
## The Basics ## The *Http* Client Demo
When we look at the broad task of connecting multiple devices to talk to each other, there really are plenty of different options.
If we zoom in a little and restrict us to only look at the options that allow a web application that runs in a browser to communicate with
a backend we are basically left with communication via the [`HTTP`](https://tools.ietf.org/html/rfc2616) or [`WebSocket`](https://tools.ietf.org/html/rfc6455) protocol.
For web apps to communicate via `HTTP` there are basically two different techniques which are `XMLHttpRequest (XHR)` and `JSONP`.
They are quite different from each other both in purpose and implementation.
Angular comes with its own built-in abstractions that allow us to exploit both techniques in a nicely well integrated way.
## Http Client
We use the Angular `Http` client to communicate via `XMLHttpRequest (XHR)`. We use the Angular `Http` client to communicate via `XMLHttpRequest (XHR)`.
We'll illustrate with a mini-version of the [tutorial](../tutorial)'s "Tour of Heroes" (ToH) application. We'll demonstrate with a mini-version of the [tutorial](../tutorial)'s "Tour of Heroes" (ToH) application.
This one gets some heroes from the server, displays them in a list, lets us add new heroes, and save them to the server. This version gets some heroes from the server, displays them in a list, lets us add new heroes, and save them to the server.
It works like this. It works like this.
figure.image-display figure.image-display
@ -99,7 +103,7 @@ figure.image-display
Components are easier to test and debug when their constructors are simple and all real work Components are easier to test and debug when their constructors are simple and all real work
(especially calling a remote server) is handled in a separate method. (especially calling a remote server) is handled in a separate method.
:marked :marked
The service `get` and `addHero` methods return an `Observable` to which we `subscribe`, The service `get` and `addHero` methods return an `Observable` of HTTP Responses to which we `subscribe`,
specifying the actions to take if a method succeeds or fails. specifying the actions to take if a method succeeds or fails.
We'll get to observables and subscription shortly. We'll get to observables and subscription shortly.
@ -131,20 +135,30 @@ figure.image-display
We pass the resource URL to `get` and it calls the server which returns We pass the resource URL to `get` and it calls the server which returns
data from the `heroes.json` file. data from the `heroes.json` file.
<a id="rxjs"></a>
The return value may surprise us. Many of us would expect a The return value may surprise us. Many of us would expect a
[promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). [promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
We'd expect to chain a call to `then()` and extract the heroes. We'd expect to chain a call to `then()` and extract the heroes.
Instead we're calling a `map()` method. Instead we're calling a `map()` method.
Clearly this is not a promise. Clearly this is not a promise.
### RxJS Observables In fact, the `http.get` method returns an **Observable** of HTTP Responses (`Observable<Response>`) from the RxJS library
The `http.get` method returns an **Observable** from the [RxJS library](https://github.com/ReactiveX/RxJS). and `map` is one of the RxJS *operators*.
.l-sub-section .callout.is-important
header HTTP GET Delayed
:marked :marked
We cover the rudiments of RxJS in the [Observables](observables.html) chapter. The `http.get` does **not send the request just yet!** This observable is
[*cold*](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/creating.md#cold-vs-hot-observables)
which means the request won't go out until something *subscribes* to the observable.
That *something* is the [HeroListComponent](#subscribe).
.l-main-section
:marked :marked
RxJS is a 3rd party library endorsed by Angular. ### RxJS Library
All of our documenations samples have installed the RxJS npm package and loaded the script in `index.html` [RxJS](https://github.com/ReactiveX/RxJS) ("Reactive Extensions") is a 3rd party library, endorsed by Angular,
that implements the [*asynchronous observable*](https://www.youtube.com/watch?v=UHI0AzD_WfY "Rob Wormald on observables") pattern.
All of our Developer Guide samples have installed the RxJS npm package and loaded the RxJS script in `index.html`
because observables are used widely in Angular applications. because observables are used widely in Angular applications.
+makeExample('server-communication/ts/index.html', 'rxjs', 'index.html')(format=".") +makeExample('server-communication/ts/index.html', 'rxjs', 'index.html')(format=".")
:marked :marked
@ -193,7 +207,7 @@ figure.image-display
This is conventional web api behavior, driven by security concerns. This is conventional web api behavior, driven by security concerns.
:marked :marked
### Do not return the response object ### Do not return the response object
Our `getHeroes()` could have returned returned the `Observable<Response>`. Our `getHeroes()` could have returned the `Observable<Response>`.
Bad idea! The point of a data service is to hide the server interaction details from consumers. Bad idea! The point of a data service is to hide the server interaction details from consumers.
The component that calls the `HeroService` wants heroes. The component that calls the `HeroService` wants heroes.
@ -221,7 +235,11 @@ figure.image-display
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'error-handling', 'app/toh/hero.service.ts')(format=".") +makeExample('server-communication/ts/app/toh/hero.service.ts', 'error-handling', 'app/toh/hero.service.ts')(format=".")
<a id="subscribe"></a>
<a id="hero-list-component"></a>
.l-main-section
:marked :marked
## Subscribe in the *HeroListComponent*
Back in the `HeroListComponent`, where we called `heroService.get`, Back in the `HeroListComponent`, where we called `heroService.get`,
we supply the `subscribe` function with a second function to handle the error message. we supply the `subscribe` function with a second function to handle the error message.
It sets an `errorMessage` variable which we've bound conditionally in the template. It sets an `errorMessage` variable which we've bound conditionally in the template.
@ -312,8 +330,8 @@ code-example(format="." language="javascript").
:marked :marked
## Fall back to Promises ## Fall back to Promises
Although the Angular `http` client API returns an `Observable` we can turn it into a Although the Angular `http` client API returns an `Observable<Response>` we can turn it into a
[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) if we prefer. [Promise<Response>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) if we prefer.
It's easy to do and a promise-based version looks much like the observable-based version in simple cases. It's easy to do and a promise-based version looks much like the observable-based version in simple cases.
.l-sub-section .l-sub-section
:marked :marked
@ -351,7 +369,7 @@ code-example(format="." language="javascript").
The promise-based `then` returns another promise. We can keep chaining more `then` and `catch` calls, getting a new promise each time. The promise-based `then` returns another promise. We can keep chaining more `then` and `catch` calls, getting a new promise each time.
The `subscribe` method returns a `Subscription`. A `Subscription` is not another observable. The `subscribe` method returns a `Subscription`. A `Subscription` is not another `Observable`.
It's the end of the line for observables. We can't call `map` on it or call `subscribe` again. It's the end of the line for observables. We can't call `map` on it or call `subscribe` again.
The `Subscription` object has a different purpose, signified by its primary method, `unsubscribe`. The `Subscription` object has a different purpose, signified by its primary method, `unsubscribe`.
@ -493,25 +511,19 @@ figure.image-display
We made no changes to the template or metadata, confining them all to the component class. We made no changes to the template or metadata, confining them all to the component class.
Let's review those changes. Let's review those changes.
### Create the search term Observable ### Create a stream of search terms
We import the `Observer` symbol from the RxJS observable library to get the type information: We're binding to the search box `keyup` event and calling the component's `search` method after each keystroke.
+makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', 'import-observer')
:marked
The first real step turns the user's search box entries into an observable stream of search terms.
We create a private `Observable` called `_searchTermStream`, driven by a *subscribe* function that
feeds user search terms to the observable.
+makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', 'observable-create')(format='.') We turn these events into an observable stream of search terms using a `Subject`
which we import from the RxJS observable library:
+makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', 'import-subject')
:marked :marked
The *subscribe* function sets the component's `search` method to a function that updates Each search term is a string, so we create a new `Subject` of type `string` called `_searchTermStream`.
the observable's `observer` with search terms. After every keystroke, the `search` method adds the the search box value to that stream
+makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', 'subscribe-fn')(format='.') via the subject's `next` method.
+makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', 'subject')(format='.')
:marked :marked
The search box `keyup` events are still bound to this `search` method in the template.
After every keystroke the binding pumps the search box value into the `_searchTermStream` observable,
creating a stream of search term strings.
### Listen for search terms ### Listen for search terms
Earlier, we passed each search term directly to the service and bound the template to the service results. Earlier, we passed each search term directly to the service and bound the template to the service results.
@ -523,7 +535,7 @@ figure.image-display
Only changed search values make it through to the service Only changed search values make it through to the service
([distinctUntilChanged](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/distinctuntilchanged.md)). ([distinctUntilChanged](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/distinctuntilchanged.md)).
The `WikipediaService` returns a separate observable of strings (`Observable<string[]>`) for each request. The `WikipediaService` returns a separate observable of string arrays (`Observable<string[]>`) for each request.
We could have multiple requests *in flight*, all awaiting the server's reply, We could have multiple requests *in flight*, all awaiting the server's reply,
which means multiple *observables-of-strings* could arrive at any moment in any order. which means multiple *observables-of-strings* could arrive at any moment in any order.