docs(server-communication): fix http after testing chapter discoveries
This commit is contained in:
parent
dd7b5176a8
commit
353abff084
|
@ -1,15 +1,21 @@
|
||||||
//#docregion
|
// #docplaster
|
||||||
import {bootstrap} from 'angular2/platform/browser';
|
// #docregion
|
||||||
|
import { bootstrap } from 'angular2/platform/browser';
|
||||||
|
// #docregion http-providers
|
||||||
|
import { HTTP_PROVIDERS } from 'angular2/http';
|
||||||
|
// #enddocregion http-providers
|
||||||
|
|
||||||
// #docregion import-rxjs
|
// #docregion import-rxjs
|
||||||
// Add all operators to Observable
|
// Add all operators to Observable
|
||||||
import 'rxjs/Rx';
|
import 'rxjs/Rx';
|
||||||
// #enddocregion import-rxjs
|
// #enddocregion import-rxjs
|
||||||
|
|
||||||
import {WikiComponent} from './wiki/wiki.component';
|
import { WikiComponent } from './wiki/wiki.component';
|
||||||
import {WikiSmartComponent} from './wiki/wiki-smart.component';
|
import { WikiSmartComponent } from './wiki/wiki-smart.component';
|
||||||
import {TohComponent} from './toh/toh.component';
|
import { TohComponent } from './toh/toh.component';
|
||||||
|
|
||||||
bootstrap(WikiComponent);
|
bootstrap(WikiComponent);
|
||||||
bootstrap(WikiSmartComponent);
|
bootstrap(WikiSmartComponent);
|
||||||
bootstrap(TohComponent);
|
// #docregion http-providers
|
||||||
|
bootstrap(TohComponent, [HTTP_PROVIDERS]);
|
||||||
|
// #enddocregion http-providers
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// ToH Promise Version
|
||||||
// #docregion
|
// #docregion
|
||||||
import {Component, OnInit} from 'angular2/core';
|
import {Component, OnInit} from 'angular2/core';
|
||||||
import {Hero} from './hero';
|
import {Hero} from './hero';
|
||||||
|
@ -5,22 +6,7 @@ import {HeroService} from './hero.service.1';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'hero-list',
|
selector: 'hero-list',
|
||||||
// #docregion template
|
templateUrl: 'app/toh/hero-list.component.html',
|
||||||
template: `
|
|
||||||
<h3>Heroes:</h3>
|
|
||||||
<ul>
|
|
||||||
<li *ngFor="#hero of heroes">
|
|
||||||
{{ hero.name }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
New Hero:
|
|
||||||
<input #newHero />
|
|
||||||
<button (click)="addHero(newHero.value); newHero.value=''">
|
|
||||||
Add Hero
|
|
||||||
</button>
|
|
||||||
<div class="error" *ngIf="errorMessage">{{errorMessage}}</div>
|
|
||||||
`,
|
|
||||||
// #enddocregion template
|
|
||||||
styles: ['.error {color:red;}']
|
styles: ['.error {color:red;}']
|
||||||
})
|
})
|
||||||
// #docregion component
|
// #docregion component
|
||||||
|
@ -29,7 +15,7 @@ export class HeroListComponent implements OnInit {
|
||||||
constructor (private _heroService: HeroService) {}
|
constructor (private _heroService: HeroService) {}
|
||||||
|
|
||||||
errorMessage: string;
|
errorMessage: string;
|
||||||
heroes:Hero[];
|
heroes: Hero[];
|
||||||
|
|
||||||
ngOnInit() { this.getHeroes(); }
|
ngOnInit() { this.getHeroes(); }
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
<!-- #docregion -->
|
||||||
|
<h3>Heroes:</h3>
|
||||||
|
<ul>
|
||||||
|
<li *ngFor="#hero of heroes">
|
||||||
|
{{ hero.name }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
New Hero:
|
||||||
|
<input #newHero />
|
||||||
|
<button (click)="addHero(newHero.value); newHero.value=''">
|
||||||
|
Add Hero
|
||||||
|
</button>
|
||||||
|
<div class="error" *ngIf="errorMessage">{{errorMessage}}</div>
|
|
@ -5,20 +5,7 @@ import {HeroService} from './hero.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'hero-list',
|
selector: 'hero-list',
|
||||||
template: `
|
templateUrl: 'app/toh/hero-list.component.html',
|
||||||
<h3>Heroes:</h3>
|
|
||||||
<ul>
|
|
||||||
<li *ngFor="#hero of heroes">
|
|
||||||
{{ hero.name }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
New Hero:
|
|
||||||
<input #newHero />
|
|
||||||
<button (click)="addHero(newHero.value); newHero.value=''">
|
|
||||||
Add Hero
|
|
||||||
</button>
|
|
||||||
<div class="error" *ngIf="errorMessage">{{errorMessage}}</div>
|
|
||||||
`,
|
|
||||||
styles: ['.error {color:red;}']
|
styles: ['.error {color:red;}']
|
||||||
})
|
})
|
||||||
// #docregion component
|
// #docregion component
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* Promise version */
|
// ToH Promise Version
|
||||||
// #docplaster
|
// #docplaster
|
||||||
|
|
||||||
// #docregion
|
// #docregion
|
||||||
import {Injectable} from 'angular2/core';
|
import {Injectable} from 'angular2/core';
|
||||||
import {Http} from 'angular2/http';
|
import {Http, Response} from 'angular2/http';
|
||||||
import {Headers, RequestOptions} from 'angular2/http';
|
import {Headers, RequestOptions} from 'angular2/http';
|
||||||
import {Hero} from './hero';
|
import {Hero} from './hero';
|
||||||
|
|
||||||
|
@ -15,27 +15,36 @@ export class HeroService {
|
||||||
private _heroesUrl = 'app/heroes.json';
|
private _heroesUrl = 'app/heroes.json';
|
||||||
|
|
||||||
// #docregion methods
|
// #docregion methods
|
||||||
getHeroes () {
|
getHeroes (): Promise<Hero[]> {
|
||||||
return this.http.get(this._heroesUrl)
|
return this.http.get(this._heroesUrl)
|
||||||
.toPromise()
|
.toPromise()
|
||||||
.then(res => <Hero[]> res.json().data, this.handleError)
|
.then(this.extractData)
|
||||||
.then(data => { console.log(data); return data; }); // eyeball results in the console
|
.catch(this.handleError);
|
||||||
}
|
}
|
||||||
|
|
||||||
addHero (name: string) : Promise<Hero> {
|
addHero (name: string): Promise<Hero> {
|
||||||
let body = JSON.stringify({ name });
|
let body = JSON.stringify({ name });
|
||||||
let headers = new Headers({ 'Content-Type': 'application/json' });
|
let headers = new Headers({ 'Content-Type': 'application/json' });
|
||||||
let options = new RequestOptions({ headers: headers });
|
let options = new RequestOptions({ headers: headers });
|
||||||
|
|
||||||
return this.http.post(this._heroesUrl, body, options)
|
return this.http.post(this._heroesUrl, body, options)
|
||||||
.toPromise()
|
.toPromise()
|
||||||
.then(res => <Hero> res.json().data)
|
.then(this.extractData)
|
||||||
.catch(this.handleError);
|
.catch(this.handleError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
private handleError (error: any) {
|
||||||
// in a real world app, we may send the error to some remote logging infrastructure
|
// In a real world app, we might send the error to remote logging infrastructure
|
||||||
console.error(error); // log to console instead
|
|
||||||
let errMsg = error.message || 'Server error';
|
let errMsg = error.message || 'Server error';
|
||||||
|
console.error(errMsg); // log to console instead
|
||||||
return Promise.reject(errMsg);
|
return Promise.reject(errMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
// #docregion
|
// #docregion
|
||||||
// #docregion v1
|
// #docregion v1
|
||||||
import {Injectable} from 'angular2/core';
|
import {Injectable} from 'angular2/core';
|
||||||
import {Http} from 'angular2/http';
|
import {Http, Response} from 'angular2/http';
|
||||||
// #enddocregion v1
|
// #enddocregion v1
|
||||||
// #docregion import-request-options
|
// #docregion import-request-options
|
||||||
import {Headers, RequestOptions} from 'angular2/http';
|
import {Headers, RequestOptions} from 'angular2/http';
|
||||||
|
@ -31,18 +31,13 @@ export class HeroService {
|
||||||
// #enddocregion endpoint
|
// #enddocregion endpoint
|
||||||
|
|
||||||
// #docregion methods
|
// #docregion methods
|
||||||
// #docregion error-handling
|
// #docregion error-handling, http-get
|
||||||
getHeroes (): Observable<Hero[]> {
|
getHeroes (): Observable<Hero[]> {
|
||||||
// #docregion http-get, http-get-v1
|
|
||||||
return this.http.get(this._heroesUrl)
|
return this.http.get(this._heroesUrl)
|
||||||
.map(this.extractData)
|
.map(this.extractData)
|
||||||
// #enddocregion v1, http-get-v1, error-handling
|
|
||||||
.do(data => console.log(data)) // eyeball results in the console
|
|
||||||
// #docregion v1, http-get-v1, error-handling
|
|
||||||
.catch(this.handleError);
|
.catch(this.handleError);
|
||||||
// #enddocregion http-get, http-get-v1
|
|
||||||
}
|
}
|
||||||
// #enddocregion error-handling
|
// #enddocregion error-handling, http-get
|
||||||
// #enddocregion v1
|
// #enddocregion v1
|
||||||
|
|
||||||
// #docregion addhero
|
// #docregion addhero
|
||||||
|
@ -62,6 +57,7 @@ export class HeroService {
|
||||||
|
|
||||||
// #docregion v1
|
// #docregion v1
|
||||||
|
|
||||||
|
// #docregion extract-data
|
||||||
private extractData(res: Response) {
|
private extractData(res: Response) {
|
||||||
if (res.status < 200 || res.status >= 300) {
|
if (res.status < 200 || res.status >= 300) {
|
||||||
throw new Error('Bad response status: ' + res.status);
|
throw new Error('Bad response status: ' + res.status);
|
||||||
|
@ -69,12 +65,13 @@ export class HeroService {
|
||||||
let body = res.json();
|
let body = res.json();
|
||||||
return body.data || { };
|
return body.data || { };
|
||||||
}
|
}
|
||||||
|
// #enddocregion extract-data
|
||||||
|
|
||||||
// #docregion error-handling
|
// #docregion error-handling
|
||||||
private handleError (error: any) {
|
private handleError (error: any) {
|
||||||
// in a real world app, we may send the error to some remote logging infrastructure
|
// In a real world app, we might send the error to remote logging infrastructure
|
||||||
console.error(error); // log to console instead
|
|
||||||
let errMsg = error.message || 'Server error';
|
let errMsg = error.message || 'Server error';
|
||||||
|
console.error(errMsg); // log to console instead
|
||||||
return Observable.throw(errMsg);
|
return Observable.throw(errMsg);
|
||||||
}
|
}
|
||||||
// #enddocregion error-handling
|
// #enddocregion error-handling
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
// ToH Promise Version
|
||||||
|
console.log ('Promise version');
|
||||||
|
|
||||||
|
import { Component } from 'angular2/core';
|
||||||
|
import { HTTP_PROVIDERS } from 'angular2/http';
|
||||||
|
|
||||||
|
import { HeroListComponent } from './hero-list.component.1';
|
||||||
|
import { HeroService } from './hero.service.1';
|
||||||
|
|
||||||
|
import { provide } from 'angular2/core';
|
||||||
|
import { XHRBackend } from 'angular2/http';
|
||||||
|
|
||||||
|
import { InMemoryBackendService,
|
||||||
|
SEED_DATA } from 'a2-in-memory-web-api/core';
|
||||||
|
import { HeroData } from '../hero-data';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-toh',
|
||||||
|
template: `
|
||||||
|
<h1>Tour of Heroes</h1>
|
||||||
|
<hero-list></hero-list>
|
||||||
|
`,
|
||||||
|
directives:[HeroListComponent],
|
||||||
|
providers: [
|
||||||
|
HTTP_PROVIDERS,
|
||||||
|
HeroService,
|
||||||
|
// in-memory web api providers
|
||||||
|
provide(XHRBackend, { useClass: InMemoryBackendService }), // in-mem server
|
||||||
|
provide(SEED_DATA, { useClass: HeroData }) // in-mem server data
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class TohComponent { }
|
|
@ -0,0 +1,44 @@
|
||||||
|
// #docplaster
|
||||||
|
|
||||||
|
// #docregion
|
||||||
|
import { Component } from 'angular2/core';
|
||||||
|
import { HTTP_PROVIDERS } from 'angular2/http';
|
||||||
|
|
||||||
|
import { HeroListComponent } from './hero-list.component';
|
||||||
|
import { HeroService } from './hero.service';
|
||||||
|
// #enddocregion
|
||||||
|
|
||||||
|
// #docregion in-mem-web-api-imports
|
||||||
|
import { provide } from 'angular2/core';
|
||||||
|
import { XHRBackend } from 'angular2/http';
|
||||||
|
|
||||||
|
// in-memory web api imports
|
||||||
|
import { InMemoryBackendService,
|
||||||
|
SEED_DATA } from 'a2-in-memory-web-api/core';
|
||||||
|
import { HeroData } from '../hero-data';
|
||||||
|
// #enddocregion in-mem-web-api-imports
|
||||||
|
// #docregion
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-toh',
|
||||||
|
// #docregion template
|
||||||
|
template: `
|
||||||
|
<h1>Tour of Heroes</h1>
|
||||||
|
<hero-list></hero-list>
|
||||||
|
`,
|
||||||
|
// #enddocregion template
|
||||||
|
directives: [HeroListComponent],
|
||||||
|
providers: [
|
||||||
|
HTTP_PROVIDERS,
|
||||||
|
HeroService,
|
||||||
|
// #enddocregion
|
||||||
|
// #docregion in-mem-web-api-providers
|
||||||
|
// in-memory web api providers
|
||||||
|
provide(XHRBackend, { useClass: InMemoryBackendService }), // in-mem server
|
||||||
|
provide(SEED_DATA, { useClass: HeroData }) // in-mem server data
|
||||||
|
// #enddocregion in-mem-web-api-providers
|
||||||
|
// #docregion
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class TohComponent { }
|
||||||
|
// #enddocregion
|
|
@ -1,24 +1,23 @@
|
||||||
// #docplaster
|
// #docplaster
|
||||||
|
|
||||||
// #docregion
|
// #docregion
|
||||||
import {Component} from 'angular2/core';
|
import { Component } from 'angular2/core';
|
||||||
import {HTTP_PROVIDERS} from 'angular2/http';
|
import { HTTP_PROVIDERS } from 'angular2/http';
|
||||||
|
|
||||||
import {Hero} from './hero';
|
import { HeroListComponent } from './hero-list.component';
|
||||||
import {HeroListComponent} from './hero-list.component';
|
import { HeroService } from './hero.service';
|
||||||
import {HeroService} from './hero.service';
|
// #enddocregion
|
||||||
//#enddocregion
|
|
||||||
|
|
||||||
//#docregion in-mem-web-api-imports
|
// #docregion in-mem-web-api-imports
|
||||||
import {provide} from 'angular2/core';
|
import { provide } from 'angular2/core';
|
||||||
import {XHRBackend} from 'angular2/http';
|
import { XHRBackend } from 'angular2/http';
|
||||||
|
|
||||||
// in-memory web api imports
|
// in-memory web api imports
|
||||||
import {InMemoryBackendService,
|
import { InMemoryBackendService,
|
||||||
SEED_DATA} from 'a2-in-memory-web-api/core';
|
SEED_DATA } from 'a2-in-memory-web-api/core';
|
||||||
import {HeroData} from '../hero-data';
|
import { HeroData } from '../hero-data';
|
||||||
// #enddocregion in-mem-web-api-imports
|
// #enddocregion in-mem-web-api-imports
|
||||||
//#docregion
|
// #docregion
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-toh',
|
selector: 'my-toh',
|
||||||
|
@ -28,17 +27,17 @@ import {HeroData} from '../hero-data';
|
||||||
<hero-list></hero-list>
|
<hero-list></hero-list>
|
||||||
`,
|
`,
|
||||||
// #enddocregion template
|
// #enddocregion template
|
||||||
directives:[HeroListComponent],
|
directives: [HeroListComponent],
|
||||||
providers: [
|
providers: [
|
||||||
HTTP_PROVIDERS,
|
HTTP_PROVIDERS,
|
||||||
HeroService,
|
HeroService,
|
||||||
//#enddocregion
|
// #enddocregion
|
||||||
//#docregion in-mem-web-api-providers
|
// #docregion in-mem-web-api-providers
|
||||||
// in-memory web api providers
|
// in-memory web api providers
|
||||||
provide(XHRBackend, { useClass: InMemoryBackendService }), // in-mem server
|
provide(XHRBackend, { useClass: InMemoryBackendService }), // in-mem server
|
||||||
provide(SEED_DATA, { useClass: HeroData }) // in-mem server data
|
provide(SEED_DATA, { useClass: HeroData }) // in-mem server data
|
||||||
//#enddocregion in-mem-web-api-providers
|
// #enddocregion in-mem-web-api-providers
|
||||||
//#docregion
|
// #docregion
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class TohComponent { }
|
export class TohComponent { }
|
||||||
|
|
|
@ -36,7 +36,7 @@ export class HeroService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleError (error: any) {
|
private handleError (error: any) {
|
||||||
// in a real world app, we may send the error to some remote logging infrastructure
|
// In a real world app, we might send the error to remote logging infrastructure
|
||||||
let errMsg = error.message || 'Server error';
|
let errMsg = error.message || 'Server error';
|
||||||
console.error(errMsg); // log to console instead
|
console.error(errMsg); // log to console instead
|
||||||
return Observable.throw(errMsg);
|
return Observable.throw(errMsg);
|
||||||
|
|
|
@ -21,7 +21,6 @@ include ../_util-fns
|
||||||
[Enabling RxJS Operators](#enable-rxjs-operators)<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>
|
|
||||||
[Send data to the server](#update)<br>
|
[Send data to the server](#update)<br>
|
||||||
[Add headers](#headers)<br>
|
[Add headers](#headers)<br>
|
||||||
[Promises instead of observables](#promises)<br>
|
[Promises instead of observables](#promises)<br>
|
||||||
|
@ -67,14 +66,20 @@ figure.image-display
|
||||||
making them available to the child components of this "Tour of Heroes" application.
|
making them available to the child components of this "Tour of Heroes" application.
|
||||||
|
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
|
:marked
|
||||||
|
Alternatively, we may choose to add the `HTTP_PROVIDERS` while bootstrapping the app:
|
||||||
|
+makeExample('server-communication/ts/app/main.ts','http-providers','app/main.ts')(format='.')
|
||||||
:marked
|
:marked
|
||||||
Learn about providers in the [Dependency Injection](dependency-injection.html) chapter.
|
Learn about providers in the [Dependency Injection](dependency-injection.html) chapter.
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
This sample only has one child, the `HeroListComponent` shown here in full:
|
This sample only has one child, the `HeroListComponent`. Here's its template:
|
||||||
+makeExample('server-communication/ts/app/toh/hero-list.component.ts', null, 'app/toh/hero-list.component.ts')
|
+makeExample('server-communication/ts/app/toh/hero-list.component.html', null, 'app/toh/hero-list.component.html (Template)')
|
||||||
:marked
|
:marked
|
||||||
The component template displays a list of heroes with the `NgFor` repeater directive.
|
The component template displays a list of heroes with the `NgFor` repeater directive.
|
||||||
|
figure.image-display
|
||||||
|
img(src='/resources/images/devguide/server-communication/hero-list.png' alt="Hero List")
|
||||||
|
:marked
|
||||||
Beneath the heroes is an input box and an *Add Hero* button where we can enter the names of new heroes
|
Beneath the heroes is an input box and an *Add Hero* button where we can enter the names of new heroes
|
||||||
and add them to the database.
|
and add them to the database.
|
||||||
We use a [local template variable](template-syntax.html#local-vars), `newHero`, to access the
|
We use a [local template variable](template-syntax.html#local-vars), `newHero`, to access the
|
||||||
|
@ -82,22 +87,26 @@ figure.image-display
|
||||||
When the user clicks the button, we pass that value to the component's `addHero` method and then
|
When the user clicks the button, we pass that value to the component's `addHero` method and then
|
||||||
clear it to make ready for a new hero name.
|
clear it to make ready for a new hero name.
|
||||||
|
|
||||||
Below the button is an optional error message.
|
Below the button is a (hidden) area for an error message.
|
||||||
|
|
||||||
a(id="oninit")
|
a(id="oninit")
|
||||||
a(id="HeroListComponent")
|
a(id="HeroListComponent")
|
||||||
:marked
|
:marked
|
||||||
### The *HeroListComponent* class
|
### The *HeroListComponent* class
|
||||||
|
Here's the component class:
|
||||||
|
+makeExample('server-communication/ts/app/toh/hero-list.component.ts','component', 'app/toh/hero-list.component.ts (class)')
|
||||||
|
:marked
|
||||||
We [inject](dependency-injection.html) the `HeroService` into the constructor.
|
We [inject](dependency-injection.html) the `HeroService` into the constructor.
|
||||||
That's the instance of the `HeroService` that we provided in the parent shell `TohComponent`.
|
That's the instance of the `HeroService` that we provided in the parent shell `TohComponent`.
|
||||||
|
|
||||||
Notice that the component **does not talk to the server directly!**
|
Notice that the component **does not talk to the server directly!**
|
||||||
The component doesn't know or care how we get the data.
|
The component doesn't know or care how we get the data.
|
||||||
Those details it delegates to the `heroService` class (which we'll get to in a moment).
|
Those details it delegates to the `heroService` class (which we'll get to in a moment).
|
||||||
This is a golden rule: *always delegate data access to a supporting service class*.
|
|
||||||
|
|
||||||
Although the component should request heroes immediately,
|
This is a golden rule: **always delegate data access to a supporting service class**.
|
||||||
we do **not** call the service `get` method in the component's constructor.
|
|
||||||
|
Although _at runtime_ the component requests heroes immediately after creation,
|
||||||
|
we do **not** call the service's `get` method in the component's constructor.
|
||||||
We call it inside the `ngOnInit` [lifecycle hook](lifecycle-hooks.html) instead
|
We call it inside the `ngOnInit` [lifecycle hook](lifecycle-hooks.html) instead
|
||||||
and count on Angular to call `ngOnInit` when it instantiates this component.
|
and count on Angular to call `ngOnInit` when it instantiates this component.
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
|
@ -106,8 +115,9 @@ a(id="HeroListComponent")
|
||||||
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` of HTTP Responses to which we `subscribe`,
|
The service `get` and `addHero` methods return an `Observable` of HTTP hero data.
|
||||||
specifying the actions to take if a method succeeds or fails.
|
We subscribe to this `Observable`,
|
||||||
|
specifying the actions to take when the request succeeds or fails.
|
||||||
We'll get to observables and subscription shortly.
|
We'll get to observables and subscription shortly.
|
||||||
|
|
||||||
With our basic intuitions about the component squared away, we can turn to development of the backend data source
|
With our basic intuitions about the component squared away, we can turn to development of the backend data source
|
||||||
|
@ -122,7 +132,7 @@ a(id="HeroListComponent")
|
||||||
In this chapter, we get the heroes from the server using Angular's own HTTP Client service.
|
In this chapter, we get the heroes from the server using Angular's own HTTP Client service.
|
||||||
Here's the new `HeroService`:
|
Here's the new `HeroService`:
|
||||||
|
|
||||||
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'v1', 'app/toh/hero.service.ts')(format=".")
|
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'v1', 'app/toh/hero.service.ts')
|
||||||
:marked
|
:marked
|
||||||
We begin by importing Angular's `Http` client service and
|
We begin by importing Angular's `Http` client service and
|
||||||
[inject it](dependency-injection.html) into the `HeroService` constructor.
|
[inject it](dependency-injection.html) into the `HeroService` constructor.
|
||||||
|
@ -133,7 +143,7 @@ a(id="HeroListComponent")
|
||||||
+makeExample('server-communication/ts/index.html', 'http', 'index.html')(format=".")
|
+makeExample('server-communication/ts/index.html', 'http', 'index.html')(format=".")
|
||||||
:marked
|
:marked
|
||||||
Look closely at how we call `http.get`
|
Look closely at how we call `http.get`
|
||||||
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'http-get-v1', 'app/toh/hero.service.ts (http.get)')(format=".")
|
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'http-get', 'app/toh/hero.service.ts (getHeroes)')(format=".")
|
||||||
:marked
|
:marked
|
||||||
We pass the resource URL to `get` and it calls the server which should return heroes.
|
We pass the resource URL to `get` and it calls the server which should return heroes.
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
|
@ -153,13 +163,6 @@ a(id="HeroListComponent")
|
||||||
|
|
||||||
In fact, the `http.get` method returns an **Observable** of HTTP Responses (`Observable<Response>`) from the RxJS library
|
In fact, the `http.get` method returns an **Observable** of HTTP Responses (`Observable<Response>`) from the RxJS library
|
||||||
and `map` is one of the RxJS *operators*.
|
and `map` is one of the RxJS *operators*.
|
||||||
.callout.is-important
|
|
||||||
header HTTP GET Delayed
|
|
||||||
:marked
|
|
||||||
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
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
|
@ -193,15 +196,37 @@ a(id="HeroListComponent")
|
||||||
It's best to add that statement early when we're bootstrapping the application.
|
It's best to add that statement early when we're bootstrapping the application.
|
||||||
:
|
:
|
||||||
+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(id="map")
|
||||||
|
a(id="extract-data")
|
||||||
:marked
|
:marked
|
||||||
<a id="map"></a>
|
### Process the response object
|
||||||
### Map the response object
|
Remember that our `getHeroes` method mapped the `http.get` response object to heroes with an `extractData` helper method:
|
||||||
Let's come back to the `HeroService` and look at the `http.get` call again to see why we needed `map()`
|
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'extract-data', 'app/toh/hero.service.ts (extractData)')(format=".")
|
||||||
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'http-get-v1', 'app/toh/hero.service.ts (http.get)')(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.
|
||||||
It takes an additional step — calling `response.json()` — to transform the bytes from the server into a JSON object.
|
To make it useful in our application we must
|
||||||
|
* check for a bad response
|
||||||
|
* parse the response data into a JSON object
|
||||||
|
.alert.is-important
|
||||||
|
:marked
|
||||||
|
*Beta alert*: error status interception and parsing may be absorbed within `http` when Angular is released.
|
||||||
|
:marked
|
||||||
|
#### Bad status codes
|
||||||
|
A status code outside the 200-300 range is 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 observable error only if `http` failed to operate (e.g., it errored internally).
|
||||||
|
|
||||||
|
Because a status code outside the 200-300 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.
|
||||||
|
|
||||||
|
#### Parse to JSON
|
||||||
|
The response data are in JSON string form.
|
||||||
|
We must parse that string into JavaScript objects which we do by calling `response.json()`.
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
This is not Angular's own design.
|
This is not Angular's own design.
|
||||||
|
@ -229,7 +254,17 @@ a(id="HeroListComponent")
|
||||||
It doesn't care where they come from.
|
It doesn't care where they come from.
|
||||||
And it certainly doesn't want to deal with a response object.
|
And it certainly doesn't want to deal with a response object.
|
||||||
|
|
||||||
<a id="error-handling"></a>
|
|
||||||
|
.callout.is-important
|
||||||
|
header HTTP GET is delayed
|
||||||
|
:marked
|
||||||
|
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).
|
||||||
|
|
||||||
|
a(id="error-handling")
|
||||||
|
:marked
|
||||||
### Always handle errors
|
### Always handle errors
|
||||||
|
|
||||||
The eagle-eyed reader may have spotted our use of the `catch` operator in conjunction with a `handleError` method.
|
The eagle-eyed reader may have spotted our use of the `catch` operator in conjunction with a `handleError` method.
|
||||||
|
@ -243,8 +278,8 @@ a(id="HeroListComponent")
|
||||||
In this simple app we provide rudimentary error handling in both the service and the component.
|
In this simple app we provide rudimentary error handling in both the service and the component.
|
||||||
|
|
||||||
We use the Observable `catch` operator on the service level.
|
We use the Observable `catch` operator on the service level.
|
||||||
It takes an error handling function with the failed `Response` object as the argument.
|
It takes an error handling function with an error object as the argument.
|
||||||
Our service handler, `errorHandler`, logs the response to the console,
|
Our service handler, `handleError`, logs the response to the console,
|
||||||
transforms the error into a user-friendly message, and returns the message in a new, failed observable via `Observable.throw`.
|
transforms the error into a user-friendly message, and returns the message in a new, failed observable via `Observable.throw`.
|
||||||
|
|
||||||
+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=".")
|
||||||
|
@ -263,19 +298,7 @@ a(id="HeroListComponent")
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
Want to see it fail? Reset the api endpoint in the `HeroService` to a bad value. Remember to restore it!
|
Want to see it fail? Reset the api endpoint in the `HeroService` to a bad value. Remember to restore it!
|
||||||
<a id="do"></a>
|
|
||||||
<a id="console-log"></a>
|
|
||||||
:marked
|
|
||||||
### Peek at results in the console
|
|
||||||
During development we're often curious about the data returned by the server.
|
|
||||||
Logging to console without disrupting the flow would be nice.
|
|
||||||
|
|
||||||
The Observable `do` operator is perfect for the job.
|
|
||||||
It passes the input through to the output while we do something with a useful side-effect such as writing to console.
|
|
||||||
Slip it into the pipeline between `map` and `catch` like this.
|
|
||||||
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'http-get', 'app/toh/hero.service.ts')(format=".")
|
|
||||||
:marked
|
|
||||||
Remember to comment it out before going to production!
|
|
||||||
|
|
||||||
<a id="update"></a>
|
<a id="update"></a>
|
||||||
<a id="post"></a>
|
<a id="post"></a>
|
||||||
|
@ -327,7 +350,7 @@ code-example(format="." language="javascript").
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
### JSON results
|
### JSON results
|
||||||
As with `get`, we extract the data from the response with `json()` and unwrap the hero via the `data` property.
|
As with `getHeroes`, we [extract the data](#extract-data) from the response with `json()` and unwrap the hero via the `data` property.
|
||||||
.alert.is-important
|
.alert.is-important
|
||||||
:marked
|
:marked
|
||||||
Know the shape of the data returned by the server.
|
Know the shape of the data returned by the server.
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 12 KiB |
Loading…
Reference in New Issue