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
|
// #docregion extract-data
|
||||||
dynamic _extractData(Response res) {
|
dynamic _extractData(Response res) {
|
||||||
if (res.statusCode < 200 || res.statusCode >= 300) {
|
|
||||||
throw new Exception('Response status: ${res.statusCode}');
|
|
||||||
}
|
|
||||||
var body = JSON.decode(res.body);
|
var body = JSON.decode(res.body);
|
||||||
// TODO: once fixed, https://github.com/adaojunior/http-in-memory-web-api/issues/1
|
// TODO: once fixed, https://github.com/adaojunior/http-in-memory-web-api/issues/1
|
||||||
// Drop the `?? body` term
|
// 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 {
|
export class HeroData {
|
||||||
createDb() {
|
createDb() {
|
||||||
let heroes = [
|
let heroes = [
|
||||||
{ "id": "1", "name": "Windstorm" },
|
{ id: '1', name: 'Windstorm' },
|
||||||
{ "id": "2", "name": "Bombasto" },
|
{ id: '2', name: 'Bombasto' },
|
||||||
{ "id": "3", "name": "Magneta" },
|
{ id: '3', name: 'Magneta' },
|
||||||
{ "id": "4", "name": "Tornado" }
|
{ id: '4', name: 'Tornado' }
|
||||||
];
|
];
|
||||||
return {heroes};
|
return {heroes};
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ import { HTTP_PROVIDERS } from '@angular/http';
|
||||||
// #enddocregion http-providers
|
// #enddocregion http-providers
|
||||||
|
|
||||||
// #docregion import-rxjs
|
// #docregion import-rxjs
|
||||||
// Add all operators to Observable
|
// Add the RxJS Observable operators we need in this app.
|
||||||
import 'rxjs/Rx';
|
import './add-rxjs-operators';
|
||||||
// #enddocregion import-rxjs
|
// #enddocregion import-rxjs
|
||||||
|
|
||||||
import { TohComponent } from './toh/toh.component';
|
import { TohComponent } from './toh/toh.component';
|
||||||
|
|
|
@ -28,7 +28,7 @@ export class HeroListComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
addHero (name: string) {
|
addHero (name: string) {
|
||||||
if (!name) {return;}
|
if (!name) { return; }
|
||||||
this.heroService.addHero(name)
|
this.heroService.addHero(name)
|
||||||
.then(
|
.then(
|
||||||
hero => this.heroes.push(hero),
|
hero => this.heroes.push(hero),
|
||||||
|
|
|
@ -31,7 +31,7 @@ export class HeroListComponent implements OnInit {
|
||||||
|
|
||||||
// #docregion addHero
|
// #docregion addHero
|
||||||
addHero (name: string) {
|
addHero (name: string) {
|
||||||
if (!name) {return;}
|
if (!name) { return; }
|
||||||
this.heroService.addHero(name)
|
this.heroService.addHero(name)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
hero => this.heroes.push(hero),
|
hero => this.heroes.push(hero),
|
||||||
|
|
|
@ -34,16 +34,14 @@ export class HeroService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private extractData(res: Response) {
|
private extractData(res: Response) {
|
||||||
if (res.status < 200 || res.status >= 300) {
|
|
||||||
throw new Error('Bad response status: ' + res.status);
|
|
||||||
}
|
|
||||||
let body = res.json();
|
let body = res.json();
|
||||||
return body.data || { };
|
return body.data || { };
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleError (error: any) {
|
private handleError (error: any) {
|
||||||
// In a real world app, we might send the error to remote logging infrastructure
|
// 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
|
console.error(errMsg); // log to console instead
|
||||||
return Promise.reject(errMsg);
|
return Promise.reject(errMsg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,9 +43,6 @@ export class HeroService {
|
||||||
|
|
||||||
// #docregion v1, extract-data
|
// #docregion v1, extract-data
|
||||||
private extractData(res: Response) {
|
private extractData(res: Response) {
|
||||||
if (res.status < 200 || res.status >= 300) {
|
|
||||||
throw new Error('Response status: ' + res.status);
|
|
||||||
}
|
|
||||||
let body = res.json();
|
let body = res.json();
|
||||||
return body.data || { };
|
return body.data || { };
|
||||||
}
|
}
|
||||||
|
@ -54,7 +51,8 @@ export class HeroService {
|
||||||
// #docregion error-handling
|
// #docregion error-handling
|
||||||
private handleError (error: any) {
|
private handleError (error: any) {
|
||||||
// In a real world app, we might use a remote logging infrastructure
|
// 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
|
console.error(errMsg); // log to console instead
|
||||||
return Observable.throw(errMsg);
|
return Observable.throw(errMsg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* tslint:disable:member-ordering */
|
||||||
// #docregion
|
// #docregion
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { JSONP_PROVIDERS } from '@angular/http';
|
import { JSONP_PROVIDERS } from '@angular/http';
|
||||||
|
@ -20,7 +21,7 @@ import { WikipediaService } from './wikipedia.service';
|
||||||
<li *ngFor="let item of items | async">{{item}}</li>
|
<li *ngFor="let item of items | async">{{item}}</li>
|
||||||
</ul>
|
</ul>
|
||||||
`,
|
`,
|
||||||
providers:[JSONP_PROVIDERS, WikipediaService]
|
providers: [JSONP_PROVIDERS, WikipediaService]
|
||||||
})
|
})
|
||||||
export class WikiSmartComponent {
|
export class WikiSmartComponent {
|
||||||
|
|
||||||
|
@ -29,13 +30,13 @@ export class WikiSmartComponent {
|
||||||
// #docregion subject
|
// #docregion subject
|
||||||
private searchTermStream = new Subject<string>();
|
private searchTermStream = new Subject<string>();
|
||||||
|
|
||||||
search(term:string) { this.searchTermStream.next(term); }
|
search(term: string) { this.searchTermStream.next(term); }
|
||||||
// #enddocregion subject
|
// #enddocregion subject
|
||||||
|
|
||||||
// #docregion observable-operators
|
// #docregion observable-operators
|
||||||
items:Observable<string[]> = this.searchTermStream
|
items: Observable<string[]> = this.searchTermStream
|
||||||
.debounceTime(300)
|
.debounceTime(300)
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.switchMap((term:string) => this.wikipediaService.search(term));
|
.switchMap((term: string) => this.wikipediaService.search(term));
|
||||||
// #enddocregion observable-operators
|
// #enddocregion observable-operators
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ import { WikipediaService } from './wikipedia.service';
|
||||||
<li *ngFor="let item of items | async">{{item}}</li>
|
<li *ngFor="let item of items | async">{{item}}</li>
|
||||||
</ul>
|
</ul>
|
||||||
`,
|
`,
|
||||||
providers:[JSONP_PROVIDERS, WikipediaService]
|
providers: [JSONP_PROVIDERS, WikipediaService]
|
||||||
})
|
})
|
||||||
export class WikiComponent {
|
export class WikiComponent {
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ export class WikipediaService {
|
||||||
let wikiUrl = 'http://en.wikipedia.org/w/api.php';
|
let wikiUrl = 'http://en.wikipedia.org/w/api.php';
|
||||||
|
|
||||||
// #docregion search-parameters
|
// #docregion search-parameters
|
||||||
var params = new URLSearchParams();
|
let params = new URLSearchParams();
|
||||||
params.set('search', term); // the user's search value
|
params.set('search', term); // the user's search value
|
||||||
params.set('action', 'opensearch');
|
params.set('action', 'opensearch');
|
||||||
params.set('format', 'json');
|
params.set('format', 'json');
|
||||||
|
|
|
@ -60,6 +60,7 @@ figure.image-display
|
||||||
:marked
|
:marked
|
||||||
Here is the `TohComponent` shell:
|
Here is the `TohComponent` shell:
|
||||||
+makeExample('server-communication/ts/app/toh/toh.component.ts', '', 'app/toh/toh.component.ts')
|
+makeExample('server-communication/ts/app/toh/toh.component.ts', '', 'app/toh/toh.component.ts')
|
||||||
|
|
||||||
block http-providers
|
block http-providers
|
||||||
:marked
|
:marked
|
||||||
As usual, we import the symbols we need. The newcomer is `HTTP_PROVIDERS`,
|
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.
|
We should include only those features that we actually need.
|
||||||
|
|
||||||
Accordingly, Angular exposes a stripped down version of `Observable` in the `rxjs/Observable` module,
|
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`.
|
such as the `map` method we called above in `getHeroes`.
|
||||||
|
|
||||||
It's up to us to add the operators we need.
|
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.
|
We could add _every_ RxJS operators with a single import statement.
|
||||||
So we'll make it easy on ourselves and enrich *Observable* with the full set of operators.
|
While that is the easiest thing to do, we'd pay a penalty in extended launch time and application size
|
||||||
It only takes one `import` statement.
|
because the full library is so big. We only use a few operators in our app.
|
||||||
It's best to add that statement early when we're bootstrapping the application.
|
|
||||||
:
|
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=".")
|
+makeExample('server-communication/ts/app/main.ts', 'import-rxjs', 'app/main.ts (import rxjs)')(format=".")
|
||||||
|
|
||||||
a#extract-data
|
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=".")
|
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'extract-data', 'app/toh/hero.service.ts (excerpt)')(format=".")
|
||||||
:marked
|
:marked
|
||||||
The `response` object does not hold our data in a form we can use directly.
|
The `response` object does not hold our data in a form we can use directly.
|
||||||
To make it useful in our application we must
|
To make it useful in our application we must parse the response data into a JSON object
|
||||||
* check the response status,
|
|
||||||
* 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
|
#### Parse to JSON
|
||||||
block parse-json
|
block parse-json
|
||||||
:marked
|
:marked
|
||||||
|
@ -597,7 +583,7 @@ block wikipedia-jsonp+
|
||||||
+makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', 'observable-operators')(format='.')
|
+makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', 'observable-operators')(format='.')
|
||||||
:marked
|
:marked
|
||||||
We wait for the user to stop typing for at least 300 milliseconds
|
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
|
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)).
|
||||||
|
|
||||||
|
@ -611,6 +597,10 @@ block wikipedia-jsonp+
|
||||||
and delivers to subscribers only the most recent search results.
|
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.
|
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
|
a#in-mem-web-api
|
||||||
.l-main-section
|
.l-main-section
|
||||||
|
|
Loading…
Reference in New Issue