docs(http): remove status 200 guard and add RxJS operators individually
This commit is contained in:
parent
f5a9edc68c
commit
594079970c
|
@ -47,9 +47,6 @@ class HeroService {
|
|||
|
||||
// #docregion extract-data
|
||||
dynamic _extractData(Response res) {
|
||||
if (res.statusCode < 200 || res.statusCode >= 300) {
|
||||
throw new Exception('Response status: ${res.statusCode}');
|
||||
}
|
||||
var body = JSON.decode(res.body);
|
||||
// TODO: once fixed, https://github.com/adaojunior/http-in-memory-web-api/issues/1
|
||||
// Drop the `?? body` term
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
// #docregion
|
||||
// import 'rxjs/Rx'; // adds ALL RxJS operators to Observable
|
||||
|
||||
// Just the Observable operators we need for THIS app.
|
||||
import 'rxjs/add/operator/catch';
|
||||
import 'rxjs/add/operator/debounceTime';
|
||||
import 'rxjs/add/operator/distinctUntilChanged';
|
||||
import 'rxjs/add/operator/map';
|
||||
import 'rxjs/add/operator/switchMap';
|
|
@ -2,10 +2,10 @@
|
|||
export class HeroData {
|
||||
createDb() {
|
||||
let heroes = [
|
||||
{ "id": "1", "name": "Windstorm" },
|
||||
{ "id": "2", "name": "Bombasto" },
|
||||
{ "id": "3", "name": "Magneta" },
|
||||
{ "id": "4", "name": "Tornado" }
|
||||
{ id: '1', name: 'Windstorm' },
|
||||
{ id: '2', name: 'Bombasto' },
|
||||
{ id: '3', name: 'Magneta' },
|
||||
{ id: '4', name: 'Tornado' }
|
||||
];
|
||||
return {heroes};
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ import { HTTP_PROVIDERS } from '@angular/http';
|
|||
// #enddocregion http-providers
|
||||
|
||||
// #docregion import-rxjs
|
||||
// Add all operators to Observable
|
||||
import 'rxjs/Rx';
|
||||
// Add the RxJS Observable operators we need in this app.
|
||||
import './add-rxjs-operators';
|
||||
// #enddocregion import-rxjs
|
||||
|
||||
import { TohComponent } from './toh/toh.component';
|
||||
|
|
|
@ -28,7 +28,7 @@ export class HeroListComponent implements OnInit {
|
|||
}
|
||||
|
||||
addHero (name: string) {
|
||||
if (!name) {return;}
|
||||
if (!name) { return; }
|
||||
this.heroService.addHero(name)
|
||||
.then(
|
||||
hero => this.heroes.push(hero),
|
||||
|
|
|
@ -31,7 +31,7 @@ export class HeroListComponent implements OnInit {
|
|||
|
||||
// #docregion addHero
|
||||
addHero (name: string) {
|
||||
if (!name) {return;}
|
||||
if (!name) { return; }
|
||||
this.heroService.addHero(name)
|
||||
.subscribe(
|
||||
hero => this.heroes.push(hero),
|
||||
|
|
|
@ -34,16 +34,14 @@ export class HeroService {
|
|||
}
|
||||
|
||||
private extractData(res: Response) {
|
||||
if (res.status < 200 || res.status >= 300) {
|
||||
throw new Error('Bad response status: ' + res.status);
|
||||
}
|
||||
let body = res.json();
|
||||
return body.data || { };
|
||||
}
|
||||
|
||||
private handleError (error: any) {
|
||||
// In a real world app, we might send the error to remote logging infrastructure
|
||||
let errMsg = error.message || 'Server error';
|
||||
// In a real world app, we might use a remote logging infrastructure
|
||||
// We'd also dig deeper into the error to get a better message
|
||||
let errMsg = error.message || error.statusText || 'Server error';
|
||||
console.error(errMsg); // log to console instead
|
||||
return Promise.reject(errMsg);
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ export class HeroService {
|
|||
// #enddocregion error-handling, http-get, v1
|
||||
|
||||
// #docregion addhero, addhero-sig
|
||||
addHero (name: string): Observable<Hero> {
|
||||
addHero (name: string): Observable<Hero> {
|
||||
// #enddocregion addhero-sig
|
||||
let body = JSON.stringify({ name });
|
||||
let headers = new Headers({ 'Content-Type': 'application/json' });
|
||||
|
@ -43,9 +43,6 @@ export class HeroService {
|
|||
|
||||
// #docregion v1, extract-data
|
||||
private extractData(res: Response) {
|
||||
if (res.status < 200 || res.status >= 300) {
|
||||
throw new Error('Response status: ' + res.status);
|
||||
}
|
||||
let body = res.json();
|
||||
return body.data || { };
|
||||
}
|
||||
|
@ -54,7 +51,8 @@ export class HeroService {
|
|||
// #docregion error-handling
|
||||
private handleError (error: any) {
|
||||
// In a real world app, we might use a remote logging infrastructure
|
||||
let errMsg = error.message || 'Server error';
|
||||
// We'd also dig deeper into the error to get a better message
|
||||
let errMsg = error.message || error.statusText || 'Server error';
|
||||
console.error(errMsg); // log to console instead
|
||||
return Observable.throw(errMsg);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* tslint:disable:member-ordering */
|
||||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
import { JSONP_PROVIDERS } from '@angular/http';
|
||||
|
@ -20,7 +21,7 @@ import { WikipediaService } from './wikipedia.service';
|
|||
<li *ngFor="let item of items | async">{{item}}</li>
|
||||
</ul>
|
||||
`,
|
||||
providers:[JSONP_PROVIDERS, WikipediaService]
|
||||
providers: [JSONP_PROVIDERS, WikipediaService]
|
||||
})
|
||||
export class WikiSmartComponent {
|
||||
|
||||
|
@ -29,13 +30,13 @@ export class WikiSmartComponent {
|
|||
// #docregion subject
|
||||
private searchTermStream = new Subject<string>();
|
||||
|
||||
search(term:string) { this.searchTermStream.next(term); }
|
||||
search(term: string) { this.searchTermStream.next(term); }
|
||||
// #enddocregion subject
|
||||
|
||||
// #docregion observable-operators
|
||||
items:Observable<string[]> = this.searchTermStream
|
||||
items: Observable<string[]> = this.searchTermStream
|
||||
.debounceTime(300)
|
||||
.distinctUntilChanged()
|
||||
.switchMap((term:string) => this.wikipediaService.search(term));
|
||||
.switchMap((term: string) => this.wikipediaService.search(term));
|
||||
// #enddocregion observable-operators
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ import { WikipediaService } from './wikipedia.service';
|
|||
<li *ngFor="let item of items | async">{{item}}</li>
|
||||
</ul>
|
||||
`,
|
||||
providers:[JSONP_PROVIDERS, WikipediaService]
|
||||
providers: [JSONP_PROVIDERS, WikipediaService]
|
||||
})
|
||||
export class WikiComponent {
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ export class WikipediaService {
|
|||
let wikiUrl = 'http://en.wikipedia.org/w/api.php';
|
||||
|
||||
// #docregion search-parameters
|
||||
var params = new URLSearchParams();
|
||||
let params = new URLSearchParams();
|
||||
params.set('search', term); // the user's search value
|
||||
params.set('action', 'opensearch');
|
||||
params.set('format', 'json');
|
||||
|
|
|
@ -60,6 +60,7 @@ figure.image-display
|
|||
:marked
|
||||
Here is the `TohComponent` shell:
|
||||
+makeExample('server-communication/ts/app/toh/toh.component.ts', '', 'app/toh/toh.component.ts')
|
||||
|
||||
block http-providers
|
||||
:marked
|
||||
As usual, we import the symbols we need. The newcomer is `HTTP_PROVIDERS`,
|
||||
|
@ -191,18 +192,26 @@ block rxjs
|
|||
We should include only those features that we actually need.
|
||||
|
||||
Accordingly, Angular exposes a stripped down version of `Observable` in the `rxjs/Observable` module,
|
||||
a version that lacks almost all operators including the ones we'd like to use here
|
||||
a version that lacks most of the operators including some we'd like to use here
|
||||
such as the `map` method we called above in `getHeroes`.
|
||||
|
||||
It's up to us to add the operators we need.
|
||||
We could add each operator, one-by-one, until we had a custom *Observable* implementation tuned
|
||||
precisely to our requirements.
|
||||
|
||||
That would be a distraction today. We're learning HTTP, not counting bytes.
|
||||
So we'll make it easy on ourselves and enrich *Observable* with the full set of operators.
|
||||
It only takes one `import` statement.
|
||||
It's best to add that statement early when we're bootstrapping the application.
|
||||
:
|
||||
|
||||
We could add _every_ RxJS operators with a single import statement.
|
||||
While that is the easiest thing to do, we'd pay a penalty in extended launch time and application size
|
||||
because the full library is so big. We only use a few operators in our app.
|
||||
|
||||
Instead, we'll import each operator, one-by-one, until we have a custom *Observable* implementation tuned
|
||||
precisely to our requirements. We'll put the `import` statements in one `app/add-rxjs-operators.ts` file.
|
||||
+makeExample('server-communication/ts/app/add-rxjs-operators.ts', null, 'app/add-rxjs-operators.ts')(format=".")
|
||||
:marked
|
||||
If we forget an operator, the compiler will warn that it's missing and we'll update this file.
|
||||
.l-sub-section
|
||||
:marked
|
||||
We don't need _all_ of these particular operators in the `HeroService` — just `map` and `catch`.
|
||||
We'll need the others later, in a *Wiki* example [below](#more-observables).
|
||||
:marked
|
||||
Finally, we import `add-rxjs-operator`_itself_ in our `main.ts`:
|
||||
+makeExample('server-communication/ts/app/main.ts', 'import-rxjs', 'app/main.ts (import rxjs)')(format=".")
|
||||
|
||||
a#extract-data
|
||||
|
@ -212,31 +221,8 @@ a#extract-data
|
|||
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'extract-data', 'app/toh/hero.service.ts (excerpt)')(format=".")
|
||||
:marked
|
||||
The `response` object does not hold our data in a form we can use directly.
|
||||
To make it useful in our application we must
|
||||
* check the response status,
|
||||
* parse the response data into a JSON object
|
||||
To make it useful in our application we must parse the response data into a JSON object
|
||||
|
||||
+ifDocsFor('ts')
|
||||
.alert.is-important
|
||||
:marked
|
||||
*Beta alert*: error status interception and parsing may be absorbed within `http` when Angular is released.
|
||||
|
||||
:marked
|
||||
#### Non-success status codes
|
||||
A status code outside the 200-299 range denotes an error from the _application point of view_
|
||||
but it is not an error from the _HTTP point of view_.
|
||||
For example, a `404 - Not Found` is a response like any other.
|
||||
The request went out; a response came back; here it is, thank you very much.
|
||||
We'd have an exception only if `#{_priv}http` failed to operate (e.g., it errored internally).
|
||||
|
||||
block non-success-status-codes
|
||||
:marked
|
||||
Because a status code outside the 200-299 range _is an error_ from the application point of view,
|
||||
we intercept it and throw, moving the observable chain to the error path.
|
||||
|
||||
The `catch` operator that is next in the `getHeroes` observable chain will handle our thrown error.
|
||||
|
||||
:marked
|
||||
#### Parse to JSON
|
||||
block parse-json
|
||||
:marked
|
||||
|
@ -597,7 +583,7 @@ block wikipedia-jsonp+
|
|||
+makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', 'observable-operators')(format='.')
|
||||
:marked
|
||||
We wait for the user to stop typing for at least 300 milliseconds
|
||||
([debounce](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/debounce.md)).
|
||||
([debounceTime](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/debounce.md)).
|
||||
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)).
|
||||
|
||||
|
@ -611,6 +597,10 @@ block wikipedia-jsonp+
|
|||
and delivers to subscribers only the most recent search results.
|
||||
|
||||
The displayed list of search results stays in sync with the user's sequence of search terms.
|
||||
.l-sub-section
|
||||
:marked
|
||||
We added the `debounceTime`, `distinctUntilChanged`, and `switchMap` operators to the RxJS `Observable` class
|
||||
in `add-rxjs-operators` as [described above](#rxjs)
|
||||
|
||||
a#in-mem-web-api
|
||||
.l-main-section
|
||||
|
|
Loading…
Reference in New Issue