docs(server-communication): heavily refactored (TS & Dart)
This commit is contained in:
parent
a4bc455030
commit
29511831cd
|
@ -0,0 +1,31 @@
|
|||
// #docplaster
|
||||
// #docregion
|
||||
import "package:angular2/core.dart" show Component;
|
||||
|
||||
import "toh/hero_list_component.dart" show HeroListComponent;
|
||||
import "wiki/wiki_component.dart" show WikiComponent;
|
||||
import "wiki/wiki_smart_component.dart" show WikiSmartComponent;
|
||||
|
||||
@Component(
|
||||
selector: "my-app",
|
||||
template: '''
|
||||
<hero-list></hero-list>
|
||||
<my-wiki></my-wiki>
|
||||
<my-wiki-smart></my-wiki-smart>
|
||||
''',
|
||||
// #enddocregion
|
||||
/*
|
||||
// #docregion http-providers
|
||||
providers: const [
|
||||
// in-memory web api provider
|
||||
const Provider(BrowserClient,
|
||||
useFactory: HttpClientBackendServiceFactory, deps: const [])],
|
||||
// #enddocregion http-providers
|
||||
*/
|
||||
// #docregion
|
||||
directives: const [
|
||||
HeroListComponent,
|
||||
WikiComponent,
|
||||
WikiSmartComponent
|
||||
])
|
||||
class AppComponent {}
|
|
@ -2,7 +2,7 @@
|
|||
import 'package:http/browser_client.dart';
|
||||
import 'package:http_in_memory_web_api/http_in_memory_web_api.dart';
|
||||
|
||||
CreateDb createDb = () => {
|
||||
CreateDb _createDb = () => {
|
||||
'heroes': [
|
||||
{"id": "1", "name": "Windstorm"},
|
||||
{"id": "2", "name": "Bombasto"},
|
||||
|
@ -12,4 +12,4 @@ CreateDb createDb = () => {
|
|||
};
|
||||
|
||||
BrowserClient HttpClientBackendServiceFactory() =>
|
||||
new HttpClientInMemoryBackendService(createDb);
|
||||
new HttpClientInMemoryBackendService(_createDb);
|
||||
|
|
|
@ -9,7 +9,7 @@ import 'hero_service.dart';
|
|||
@Component(
|
||||
selector: 'hero-list',
|
||||
templateUrl: 'hero_list_component.html',
|
||||
styles: const ['.error {color:red;}'])
|
||||
providers: const [HeroService])
|
||||
// #docregion component
|
||||
class HeroListComponent implements OnInit {
|
||||
final HeroService _heroService;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<!-- #docregion -->
|
||||
<h1>Tour of Heroes</h1>
|
||||
<h3>Heroes:</h3>
|
||||
<ul>
|
||||
<li *ngFor="let hero of heroes">
|
||||
|
|
|
@ -15,7 +15,9 @@ class HeroService {
|
|||
// #enddocregion endpoint, http-get
|
||||
final BrowserClient _http;
|
||||
|
||||
// #docregion ctor
|
||||
HeroService(this._http);
|
||||
// #enddocregion ctor
|
||||
|
||||
// #docregion methods, error-handling, http-get
|
||||
Future<List<Hero>> getHeroes() async {
|
||||
|
@ -57,6 +59,7 @@ class HeroService {
|
|||
|
||||
Exception _handleError(dynamic e) {
|
||||
// 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
|
||||
print(e); // log to console instead
|
||||
return new Exception('Server error; cause: $e');
|
||||
}
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
// #docplaster
|
||||
// #docregion
|
||||
import 'package:angular2/core.dart';
|
||||
import 'package:http/browser_client.dart';
|
||||
|
||||
import 'hero_list_component.dart';
|
||||
import 'hero_service.dart';
|
||||
// #enddocregion
|
||||
// #docregion in-mem-web-api
|
||||
/* ... */
|
||||
import 'package:server_communication/hero_data.dart';
|
||||
// #docregion
|
||||
|
||||
@Component(
|
||||
// #enddocregion in-mem-web-api
|
||||
selector: 'my-toh',
|
||||
// #docregion template
|
||||
template: '''
|
||||
<h1>Tour of Heroes</h1>
|
||||
<hero-list></hero-list>
|
||||
''',
|
||||
// #enddocregion template
|
||||
directives: const [HeroListComponent],
|
||||
// #enddocregion
|
||||
// #docregion in-mem-web-api
|
||||
/* ... */
|
||||
// #docregion
|
||||
providers: const [
|
||||
HeroService,
|
||||
// #enddocregion
|
||||
// in-memory web api providers
|
||||
const Provider(BrowserClient,
|
||||
useFactory: HttpClientBackendServiceFactory)
|
||||
// #docregion
|
||||
])
|
||||
class TohComponent {}
|
|
@ -12,11 +12,15 @@ dependencies:
|
|||
jsonpadding: ^0.1.0
|
||||
stream_transformers: ^0.3.0+3
|
||||
http_in_memory_web_api: ^0.0.1
|
||||
# #docregion transformers
|
||||
transformers:
|
||||
- angular2:
|
||||
platform_directives: 'package:angular2/common.dart#CORE_DIRECTIVES'
|
||||
platform_pipes: 'package:angular2/common.dart#COMMON_PIPES'
|
||||
platform_directives:
|
||||
- 'package:angular2/common.dart#CORE_DIRECTIVES'
|
||||
platform_pipes:
|
||||
- 'package:angular2/common.dart#COMMON_PIPES'
|
||||
entry_points: 'web/main.dart'
|
||||
resolved_identifiers:
|
||||
BrowserClient: 'package:http/browser_client.dart'
|
||||
- dart_to_js_script_rewriter
|
||||
# #enddocregion transformers
|
||||
|
|
|
@ -6,15 +6,14 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<link rel="stylesheet" href="sample.css">
|
||||
|
||||
<script defer src="main.dart" type="application/dart"></script>
|
||||
<script defer src="packages/browser/dart.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<my-toh>ToH Loading...</my-toh>
|
||||
<my-wiki>Wiki Loading...</my-wiki>
|
||||
<my-wiki-smart>WikiSmart Loading...</my-wiki-smart>
|
||||
<my-app>Loading...</my-app>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
|
@ -1,12 +1,34 @@
|
|||
// #docregion
|
||||
import 'package:angular2/platform/browser.dart';
|
||||
// #docplaster
|
||||
// #docregion final
|
||||
import 'package:angular2/core.dart' show Provider;
|
||||
// #docregion v1
|
||||
import 'package:angular2/platform/browser.dart' show bootstrap;
|
||||
// #docregion http-providers
|
||||
import 'package:http/browser_client.dart' show BrowserClient;
|
||||
// #enddocregion http-providers
|
||||
|
||||
import 'package:server_communication/toh/toh_component.dart';
|
||||
import 'package:server_communication/wiki/wiki_component.dart';
|
||||
import 'package:server_communication/wiki/wiki_smart_component.dart';
|
||||
import 'package:server_communication/app_component.dart';
|
||||
// #enddocregion v1
|
||||
// #docregion in-mem-web-api-imports
|
||||
import "package:server_communication/hero_data.dart";
|
||||
|
||||
main() {
|
||||
bootstrap(TohComponent);
|
||||
bootstrap(WikiComponent);
|
||||
bootstrap(WikiSmartComponent);
|
||||
// #enddocregion in-mem-web-api-imports
|
||||
// #docregion in-mem-web-api-providers
|
||||
void main() {
|
||||
bootstrap(AppComponent, const [
|
||||
// in-memory web api provider
|
||||
const Provider(BrowserClient,
|
||||
useFactory: HttpClientBackendServiceFactory, deps: const [])
|
||||
// TODO: drop `deps` once fix lands for
|
||||
// https://github.com/angular/angular/issues/5266
|
||||
]);
|
||||
}
|
||||
// #enddocregion final, in-mem-web-api-providers
|
||||
/*
|
||||
// #docregion v1
|
||||
|
||||
void main() {
|
||||
bootstrap(AppComponent, const [BrowserClient]);
|
||||
}
|
||||
// #enddocregion v1
|
||||
*/
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
.error {color:red;}
|
|
@ -4,69 +4,62 @@ describe('Server Communication', function () {
|
|||
browser.get('');
|
||||
});
|
||||
|
||||
describe('Tour of Heroes e2e tests', function () {
|
||||
|
||||
var _initialHeroCount = 4;
|
||||
var _newHeroName = 'Mr. IQ';
|
||||
var _heroCountAfterAdd = 5;
|
||||
|
||||
it('should display ' + _initialHeroCount + ' heroes after init', function () {
|
||||
var myTohComp = element(by.tagName('my-toh'));
|
||||
expect(myTohComp).toBeDefined('<my-toh> must exist');
|
||||
var heroListComp = myTohComp.element(by.tagName('hero-list'));
|
||||
describe('Tour of Heroes (Observable)', function () {
|
||||
|
||||
var initialHeroCount = 4;
|
||||
var newHeroName = 'Mr. IQ';
|
||||
var heroCountAfterAdd = 5;
|
||||
|
||||
var heroListComp = element(by.tagName('hero-list'));
|
||||
var addButton = heroListComp.element(by.tagName('button'));
|
||||
var heroTags = heroListComp.all(by.tagName('li'));
|
||||
var heroNameInput = heroListComp.element(by.tagName('input'));
|
||||
|
||||
it('should exist', function() {
|
||||
expect(heroListComp).toBeDefined('<hero-list> must exist');
|
||||
var heroTags = heroListComp.all(by.tagName('li'));
|
||||
expect(heroTags.count()).toBe(_initialHeroCount);
|
||||
});
|
||||
|
||||
|
||||
it('should display ' + initialHeroCount + ' heroes after init', function () {
|
||||
expect(heroTags.count()).toBe(initialHeroCount);
|
||||
});
|
||||
|
||||
it('should not add hero with empty name', function () {
|
||||
var myTohComp = element(by.tagName('my-toh'));
|
||||
expect(myTohComp).toBeDefined('<my-toh> must exist');
|
||||
var addButton = myTohComp.element(by.tagName('button'));
|
||||
expect(addButton).toBeDefined('"Add Hero" button must be defined');
|
||||
addButton.click().then(function() {
|
||||
var heroListComp = myTohComp.element(by.tagName('hero-list'));
|
||||
var heroTags = heroListComp.all(by.tagName('li'));
|
||||
expect(heroTags.count()).toBe(_initialHeroCount, 'No new hero should be added');
|
||||
expect(heroTags.count()).toBe(initialHeroCount, 'No new hero should be added');
|
||||
});
|
||||
})
|
||||
|
||||
it('should add a new hero to the list', function () {
|
||||
var myTohComp = element(by.tagName('my-toh'));
|
||||
expect(myTohComp).toBeDefined('<my-toh> must exist');
|
||||
var heroNameInput = myTohComp.element(by.tagName('input'));
|
||||
expect(heroNameInput).toBeDefined('<input> for hero name must exist');
|
||||
var addButton = myTohComp.element(by.tagName('button'));
|
||||
expect(addButton).toBeDefined('"Add Hero" button must be defined');
|
||||
sendKeys(heroNameInput, _newHeroName);
|
||||
sendKeys(heroNameInput, newHeroName);
|
||||
addButton.click().then(function() {
|
||||
var heroListComp = myTohComp.element(by.tagName('hero-list'));
|
||||
var heroTags = heroListComp.all(by.tagName('li'));
|
||||
expect(heroTags.count()).toBe(_heroCountAfterAdd, 'A new hero should be added');
|
||||
var newHeroInList = heroTags.get(_heroCountAfterAdd - 1).getText();
|
||||
expect(newHeroInList).toBe(_newHeroName, 'The hero should be added to the end of the list');
|
||||
expect(heroTags.count()).toBe(heroCountAfterAdd, 'A new hero should be added');
|
||||
var newHeroInList = heroTags.get(heroCountAfterAdd - 1).getText();
|
||||
expect(newHeroInList).toBe(newHeroName, 'The hero should be added to the end of the list');
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
describe('Wikipedia Demo e2e tests', function () {
|
||||
|
||||
|
||||
describe('Wikipedia Demo', function () {
|
||||
|
||||
it('should initialize the demo with empty result list', function () {
|
||||
var myWikiComp = element(by.tagName('my-wiki'));
|
||||
expect(myWikiComp).toBeDefined('<my-wiki> must exist');
|
||||
var resultList = myWikiComp.all(by.tagName('li'));
|
||||
expect(resultList.count()).toBe(0, 'result list must be empty');
|
||||
});
|
||||
|
||||
|
||||
describe('Fetches after each keystroke', function () {
|
||||
it('should fetch results after "B"', function(done) {
|
||||
testForRefreshedResult('B', done);
|
||||
});
|
||||
|
||||
|
||||
it('should fetch results after "Ba"', function(done) {
|
||||
testForRefreshedResult('a', done);
|
||||
});
|
||||
|
||||
|
||||
it('should fetch results after "Bas"', function(done) {
|
||||
testForRefreshedResult('s', done);
|
||||
});
|
||||
|
@ -75,13 +68,13 @@ describe('Server Communication', function () {
|
|||
testForRefreshedResult('ic', done);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function testForRefreshedResult(keyPressed, done) {
|
||||
testForResult('my-wiki', keyPressed, false, done)
|
||||
}
|
||||
});
|
||||
|
||||
describe('Smarter Wikipedia Demo e2e tests', function () {
|
||||
|
||||
describe('Smarter Wikipedia Demo', function () {
|
||||
|
||||
it('should initialize the demo with empty result list', function () {
|
||||
var myWikiSmartComp = element(by.tagName('my-wiki-smart'));
|
||||
|
@ -93,40 +86,40 @@ describe('Server Communication', function () {
|
|||
it('should fetch results after "Java"', function(done) {
|
||||
testForNewResult('Java', done);
|
||||
});
|
||||
|
||||
|
||||
it('should fetch results after "JavaS"', function(done) {
|
||||
testForStaleResult('S', done);
|
||||
});
|
||||
|
||||
|
||||
it('should fetch results after "JavaSc"', function(done) {
|
||||
testForStaleResult('c', done);
|
||||
});
|
||||
|
||||
|
||||
it('should fetch results after "JavaScript"', function(done) {
|
||||
testForStaleResult('ript', done);
|
||||
});
|
||||
|
||||
|
||||
|
||||
function testForNewResult(keyPressed, done) {
|
||||
testForResult('my-wiki-smart', keyPressed, false, done)
|
||||
}
|
||||
|
||||
function testForStaleResult(keyPressed, done) {
|
||||
testForResult('my-wiki-smart', keyPressed, true, done)
|
||||
testForResult('my-wiki-smart', keyPressed, true, done)
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
function testForResult(componentTagName, keyPressed, hasListBeforeSearch, done) {
|
||||
var searchWait = 1000; // Wait for wikipedia but not so long that tests timeout
|
||||
var wikiComponent = element(by.tagName(componentTagName));
|
||||
expect(wikiComponent).toBeDefined('<' + componentTagName + '> must exist');
|
||||
var searchBox = wikiComponent.element(by.tagName('input'));
|
||||
expect(searchBox).toBeDefined('<input> for search must exist');
|
||||
|
||||
|
||||
searchBox.sendKeys(keyPressed).then(function () {
|
||||
var resultList = wikiComponent.all(by.tagName('li'));
|
||||
|
||||
|
||||
if (hasListBeforeSearch) {
|
||||
expect(resultList.count()).toBeGreaterThan(0, 'result list should not be empty before search');
|
||||
}
|
||||
|
@ -137,5 +130,5 @@ describe('Server Communication', function () {
|
|||
}, searchWait);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
// #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';
|
|
@ -0,0 +1,37 @@
|
|||
// #docplaster
|
||||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
// #docregion import-rxjs
|
||||
// Add the RxJS Observable operators we need in this app.
|
||||
import './rxjs-operators';
|
||||
// #enddocregion import-rxjs
|
||||
|
||||
import { HeroListComponent } from './toh/hero-list.component';
|
||||
import { HeroListPromiseComponent } from './toh/hero-list.component.promise';
|
||||
|
||||
import { WikiComponent } from './wiki/wiki.component';
|
||||
import { WikiSmartComponent } from './wiki/wiki-smart.component';
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `
|
||||
<hero-list></hero-list>
|
||||
<hero-list-promise></hero-list-promise>
|
||||
<my-wiki></my-wiki>
|
||||
<my-wiki-smart></my-wiki-smart>
|
||||
`,
|
||||
// #enddocregion
|
||||
/*
|
||||
// #docregion http-providers
|
||||
providers: [ HTTP_PROVIDERS ]
|
||||
// #enddocregion http-providers
|
||||
*/
|
||||
// #docregion
|
||||
directives: [
|
||||
HeroListComponent, HeroListPromiseComponent,
|
||||
WikiComponent, WikiSmartComponent
|
||||
]
|
||||
})
|
||||
export class AppComponent { }
|
||||
// #enddocregion
|
|
@ -1,21 +1,30 @@
|
|||
// #docplaster
|
||||
// #docregion
|
||||
// #docregion final
|
||||
// Imports for loading & configuring the in-memory web api
|
||||
import { provide } from '@angular/core';
|
||||
import { XHRBackend } from '@angular/http';
|
||||
|
||||
import { InMemoryBackendService,
|
||||
SEED_DATA } from 'angular2-in-memory-web-api/core';
|
||||
import { HeroData } from './hero-data';
|
||||
|
||||
// The usual bootstrapping imports
|
||||
// #docregion v1
|
||||
import { bootstrap } from '@angular/platform-browser-dynamic';
|
||||
// #docregion http-providers
|
||||
import { HTTP_PROVIDERS } from '@angular/http';
|
||||
// #enddocregion http-providers
|
||||
|
||||
// #docregion import-rxjs
|
||||
// Add the RxJS Observable operators we need in this app.
|
||||
import './add-rxjs-operators';
|
||||
// #enddocregion import-rxjs
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
import { TohComponent } from './toh/toh.component';
|
||||
import { WikiComponent } from './wiki/wiki.component';
|
||||
import { WikiSmartComponent } from './wiki/wiki-smart.component';
|
||||
|
||||
// #docregion http-providers
|
||||
bootstrap(TohComponent, [HTTP_PROVIDERS]);
|
||||
// #enddocregion http-providers
|
||||
bootstrap(WikiComponent);
|
||||
bootstrap(WikiSmartComponent);
|
||||
// #enddocregion v1, final
|
||||
/*
|
||||
// #docregion v1
|
||||
bootstrap(AppComponent, [ HTTP_PROVIDERS ]);
|
||||
// #enddocregion v1
|
||||
*/
|
||||
// #docregion final
|
||||
bootstrap(AppComponent, [
|
||||
HTTP_PROVIDERS,
|
||||
provide(XHRBackend, { useClass: InMemoryBackendService }), // in-mem server
|
||||
provide(SEED_DATA, { useClass: HeroData }) // in-mem server data
|
||||
]);
|
||||
// #enddocregion final
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
// #docregion
|
||||
// import 'rxjs/Rx'; // adds ALL RxJS statics & operators to Observable
|
||||
|
||||
// See node_module/rxjs/Rxjs.js
|
||||
// Import just the rxjs statics and operators we need for THIS app.
|
||||
|
||||
// Statics
|
||||
import 'rxjs/add/observable/throw';
|
||||
|
||||
// Operators
|
||||
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';
|
||||
import 'rxjs/add/operator/toPromise';
|
|
@ -1,4 +1,5 @@
|
|||
<!-- #docregion -->
|
||||
<h1>Tour of Heroes ({{mode}})</h1>
|
||||
<h3>Heroes:</h3>
|
||||
<ul>
|
||||
<li *ngFor="let hero of heroes">
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
// ToH Promise Version
|
||||
// #docregion
|
||||
// Promise Version
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Hero } from './hero';
|
||||
import { HeroService } from './hero.service.1';
|
||||
import { HeroService } from './hero.service.promise';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-list',
|
||||
selector: 'hero-list-promise',
|
||||
templateUrl: 'app/toh/hero-list.component.html',
|
||||
styles: ['.error {color:red;}']
|
||||
providers: [ HeroService ]
|
||||
})
|
||||
// #docregion component
|
||||
export class HeroListComponent implements OnInit {
|
||||
export class HeroListPromiseComponent implements OnInit {
|
||||
|
||||
constructor (private heroService: HeroService) {}
|
||||
|
||||
errorMessage: string;
|
||||
heroes: Hero[];
|
||||
mode = 'Promise';
|
||||
|
||||
ngOnInit() { this.getHeroes(); }
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
// #docregion
|
||||
// Observable Version
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
import { Hero } from './hero';
|
||||
import { HeroService } from './hero.service';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-list',
|
||||
templateUrl: 'app/toh/hero-list.component.html',
|
||||
styles: ['.error {color:red;}']
|
||||
providers: [ HeroService ]
|
||||
})
|
||||
// #docregion component
|
||||
export class HeroListComponent implements OnInit {
|
||||
|
@ -15,7 +15,8 @@ export class HeroListComponent implements OnInit {
|
|||
constructor (private heroService: HeroService) {}
|
||||
|
||||
errorMessage: string;
|
||||
heroes:Hero[];
|
||||
heroes: Hero[];
|
||||
mode = 'Observable';
|
||||
|
||||
ngOnInit() { this.getHeroes(); }
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// ToH Promise Version
|
||||
// #docplaster
|
||||
|
||||
// #docregion
|
||||
// Promise Version
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Http, Response } from '@angular/http';
|
||||
import { Headers, RequestOptions } from '@angular/http';
|
||||
|
@ -41,7 +40,8 @@ export class HeroService {
|
|||
private handleError (error: any) {
|
||||
// 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';
|
||||
let errMsg = (error.message) ? error.message :
|
||||
error.status ? `${error.status} - ${error.statusText}` : 'Server error';
|
||||
console.error(errMsg); // log to console instead
|
||||
return Promise.reject(errMsg);
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
// #docplaster
|
||||
// #docregion
|
||||
// Observable Version
|
||||
// #docregion v1
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Http, Response } from '@angular/http';
|
||||
|
@ -14,7 +15,9 @@ import { Observable } from 'rxjs/Observable';
|
|||
|
||||
@Injectable()
|
||||
export class HeroService {
|
||||
// #docregion ctor
|
||||
constructor (private http: Http) {}
|
||||
// #enddocregion ctor
|
||||
|
||||
// #docregion endpoint
|
||||
private heroesUrl = 'app/heroes'; // URL to web API
|
||||
|
@ -52,7 +55,8 @@ export class HeroService {
|
|||
private handleError (error: any) {
|
||||
// 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';
|
||||
let errMsg = (error.message) ? error.message :
|
||||
error.status ? `${error.status} - ${error.statusText}` : 'Server error';
|
||||
console.error(errMsg); // log to console instead
|
||||
return Observable.throw(errMsg);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// #docregion
|
||||
export class Hero {
|
||||
constructor(
|
||||
public id:number,
|
||||
public name:string) { }
|
||||
public id: number,
|
||||
public name: string) { }
|
||||
}
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
// #docplaster
|
||||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
import { HTTP_PROVIDERS } from '@angular/http';
|
||||
|
||||
import { HeroListComponent } from './hero-list.component';
|
||||
import { HeroService } from './hero.service';
|
||||
// #enddocregion
|
||||
|
||||
// #docregion in-mem-web-api-imports
|
||||
import { provide } from '@angular/core';
|
||||
import { XHRBackend } from '@angular/http';
|
||||
|
||||
// in-memory web api imports
|
||||
import { InMemoryBackendService,
|
||||
SEED_DATA } from 'angular2-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
|
|
@ -6,6 +6,8 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<link rel="stylesheet" href="sample.css">
|
||||
|
||||
|
||||
<!-- Polyfill(s) for older browsers -->
|
||||
<script src="node_modules/es6-shim/es6-shim.min.js"></script>
|
||||
|
@ -21,9 +23,7 @@
|
|||
</head>
|
||||
|
||||
<body>
|
||||
<my-toh>ToH Loading...</my-toh>
|
||||
<my-wiki>Wiki Loading...</my-wiki>
|
||||
<my-wiki-smart>WikiSmart Loading...</my-wiki-smart>
|
||||
<my-app>Loading...</my-app>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
.error {color:red;}
|
|
@ -2,10 +2,40 @@ extends ../../../ts/latest/guide/server-communication.jade
|
|||
|
||||
block includes
|
||||
include ../_util-fns
|
||||
- var _Http = 'BrowserClient';
|
||||
- var _Angular_Http = 'Dart <code>BrowserClient</code>'
|
||||
- var _httpUrl = 'https://pub.dartlang.org/packages/http'
|
||||
- var _Angular_http_library = 'Dart <a href="!{_httpUrl}"><b>http</b> library</a>'
|
||||
|
||||
block demos-list
|
||||
li HTTP client: Tour of Heroes
|
||||
li JSONP client: Wikipedia to fetch data from a service that doesn't support CORS (under development)
|
||||
|
||||
block rxjs-import
|
||||
//- N/A
|
||||
|
||||
block http-client
|
||||
|
||||
block system-config-of-http
|
||||
//- N/A
|
||||
|
||||
block http-providers
|
||||
//- TODO(chalin): mention the Angular transformer resolved_identifiers.
|
||||
//- Maybe not yet at this point in the chapter.
|
||||
:marked
|
||||
Actually, it is unnecessary to include `BrowserClient` in the list of providers.
|
||||
***But*** as is mentioned in the *Angular 2 Dart Transformer* [wiki page][ng2dtri],
|
||||
the template compiler _generates_ dependency injection code, hence all the
|
||||
identifiers used in DI have to be collected by the Angular 2 transformer
|
||||
so that the libraries containing these identifiers can be transformed.
|
||||
|
||||
Unless special steps are taken, Dart libraries like `http`
|
||||
are not transformed. To ensure that the `BrowserClient` identifier is available
|
||||
for DI, we must add a `resolved_identifiers` parameter to the `angular2`
|
||||
transformer in `pubspec.yaml`:
|
||||
|
||||
[ng2dtri]: https://github.com/angular/angular/wiki/Angular-2-Dart-Transformer#resolved_identifiers
|
||||
|
||||
- var stylePattern = { pnk: /(resolved_identifiers:|Browser.*)/gm, otl: /(- angular2:)|(transformers:)/g };
|
||||
+makeExcerpt('pubspec.yaml', 'transformers', 'pubspec.yaml (transformers)', stylePattern)
|
||||
|
||||
block getheroes-and-addhero
|
||||
:marked
|
||||
|
@ -20,27 +50,13 @@ block getheroes-and-addhero
|
|||
programming in Dart, or the tutorial on
|
||||
[_Asynchronous Programming: Futures_](https://www.dartlang.org/docs/tutorials/futures/).
|
||||
|
||||
block http-client-service
|
||||
:marked
|
||||
The imported `BrowserClient` client service gets
|
||||
[injected](dependency-injection.html) into the `HeroService` constructor.
|
||||
Note that `BrowserClient` is not part of the Angular core.
|
||||
It's an optional service provided by the Dart
|
||||
[`http` package](https://pub.dartlang.org/packages/http).
|
||||
|
||||
block rxjs
|
||||
//- N/A
|
||||
|
||||
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 test for this condition and throw an
|
||||
exception when detected.
|
||||
|
||||
block parse-json
|
||||
:marked
|
||||
The response data are in JSON string form.
|
||||
We must parse that string into JavaScript objects which we do by calling
|
||||
We must parse that string into Objects which we do by calling
|
||||
the `JSON.decode()` method from the `dart:convert` library.
|
||||
|
||||
block error-handling
|
||||
|
@ -58,14 +74,13 @@ block hero-list-comp-add-hero
|
|||
Back in the `HeroListComponent`, we see that *its* `addHero()`
|
||||
awaits for the *service's* asynchronous `addHero()` to return, and when it does,
|
||||
the new hero is added to the `heroes` list for presentation to the user.
|
||||
+makeExample('server-communication/ts/app/toh/hero-list.component.ts', 'addHero', 'app/toh/hero-list.component.ts (addHero)')(format=".")
|
||||
|
||||
block promises
|
||||
//- N/A
|
||||
|
||||
block wikipedia-jsonp+
|
||||
:marked
|
||||
Wikipedia offers both `CORS` and `JSONP` search APIs.
|
||||
Wikipedia offers a modern `CORS` API and a legacy `JSONP` search API.
|
||||
.alert.is-important
|
||||
:marked
|
||||
The remaining content of this section is coming soon.
|
||||
|
@ -77,6 +92,4 @@ block redirect-to-web-api
|
|||
:marked
|
||||
To achieve this, we have Angular inject an in-memory web API server
|
||||
instance as a provider for the `BrowserClient`. This is possible because
|
||||
the in-memory web API server class extends `BrowserClient`. Here are the
|
||||
pertinent details, excerpt from `TohComponent`:
|
||||
+makeExample('server-communication/ts/app/toh/toh.component.ts', 'in-mem-web-api', 'app/toh.component.ts (excerpt)')(format=".")
|
||||
the in-memory web API server class extends `BrowserClient`.
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
block includes
|
||||
include ../_util-fns
|
||||
- var _Http = 'Http'; // Angular `Http` library name.
|
||||
- var _Angular_Http = 'Angular <code>Http</code>'
|
||||
- var _Angular_http_library = 'Angular HTTP library'
|
||||
|
||||
:marked
|
||||
[HTTP](https://tools.ietf.org/html/rfc2616) is the primary protocol for browser/server communication.
|
||||
.l-sub-section
|
||||
|
@ -16,7 +20,7 @@ block includes
|
|||
as we'll learn in this chapter covering:
|
||||
|
||||
ul
|
||||
li #[a(href="#http-client") Http client sample overview]
|
||||
li #[a(href="#http-client") HTTP client sample overview]
|
||||
li #[a(href="#fetch-data") Fetch data with http.get]
|
||||
+ifDocsFor('ts')
|
||||
li #[a(href="#rxjs") RxJS Observable of HTTP Responses]
|
||||
|
@ -35,79 +39,110 @@ ul
|
|||
p.
|
||||
We illustrate these topics with code that you can
|
||||
#[+liveExampleLink2('run live in a browser', 'server-communication')].
|
||||
|
||||
.l-main-section
|
||||
h1 Demos
|
||||
p This chapter describes server communication with the help of the following demos
|
||||
ul
|
||||
block demos-list
|
||||
li HTTP client: Tour of Heroes with Observables
|
||||
li HTTP client: Tour of Heroes with #{_Promise}s
|
||||
li JSONP client: Wikipedia to fetch data from a service that doesn't support CORS
|
||||
li JSONP client: Wikipedia using observable operators to reduce server calls
|
||||
:marked
|
||||
These demos are orchestrated by the root `AppComponent`
|
||||
+makeExample('server-communication/ts/app/app.component.ts', null, 'app/app.component.ts')
|
||||
|
||||
block rxjs-import
|
||||
:marked
|
||||
There is nothing remarkable here _except_ for the import of RxJS operators.
|
||||
+makeExample('server-communication/ts/app/app.component.ts', 'import-rxjs')(format='.')
|
||||
:marked
|
||||
We'll talk about that [below](#rxjs) when we're ready to explore observables.
|
||||
:marked
|
||||
First, we have to configure our application to use server communication facilities.
|
||||
|
||||
.l-main-section
|
||||
h1#http-providers Providing HTTP Services
|
||||
:marked
|
||||
We use the !{_Angular_Http} client to communicate with a server using a familiar HTTP request/response protocol.
|
||||
The `!{_Http}` client is one of a family of services in the !{_Angular_http_library}.
|
||||
block system-config-of-http
|
||||
.l-sub-section
|
||||
:marked
|
||||
SystemJS knows how to load services from the !{_Angular_http_library} when we import from the `@angular/http` module
|
||||
because we registered that module name in the `system.config` file.
|
||||
:marked
|
||||
Before we can use the `!{_Http}` client , we'll have to register it as a service provider with the Dependency Injection system.
|
||||
.l-sub-section
|
||||
:marked
|
||||
Learn about providers in the [Dependency Injection](dependency-injection.html) chapter.
|
||||
|
||||
p In this demo, we register providers in the #[code bootstrap] method of #[code #[+adjExPath('app/main.ts')]].
|
||||
+makeExample('server-communication/ts/app/main.ts', 'v1', 'app/main.ts (v1)')(format='.')
|
||||
|
||||
block http-providers
|
||||
:marked
|
||||
We begin by importing the symbols we need, most of them familiar by now. The newcomer is `HTTP_PROVIDERS`,
|
||||
a collection of service providers from the Angular HTTP library.
|
||||
|
||||
We register HTTP providers in the bootstrap method by passing them in an array as the second parameter after the root component.
|
||||
|
||||
### Why register in *bootstrap*?
|
||||
|
||||
We prefer to register application-wide providers in the metadata `providers` array
|
||||
of the root `AppComponent` like this:
|
||||
+makeExample('server-communication/ts/app/app.component.ts','http-providers')(format='.')
|
||||
:marked
|
||||
Here we register the providers in the `bootstrap` method in the `main.ts` file. Why?
|
||||
|
||||
This is a *sample application* that doesn't talk to a real server.
|
||||
We're going to reconfigure the (typically-hidden) `XhrBackend` service with a fake provider
|
||||
that fetches and saves sample data from an in-memory data store.
|
||||
This replacement service is called the [*in-memory web api*](#in-mem-web-api).
|
||||
|
||||
Such sleight-of-hand is something the root application component should *not* know about.
|
||||
For this reason, and this reason *only*, we hide it *above* the `AppComponent` in `main.ts`.
|
||||
|
||||
.l-main-section
|
||||
a#http-client
|
||||
:marked
|
||||
## The *Http* Client Demo
|
||||
# The Tour of Heroes *HTTP* Client Demo
|
||||
|
||||
We use the Angular `Http` client to communicate via `XMLHttpRequest (XHR)`.
|
||||
|
||||
We'll demonstrate with a mini-version of the [tutorial](../tutorial)'s "Tour of Heroes" (ToH) application.
|
||||
Our first demo is a mini-version of the [tutorial](../tutorial)'s "Tour of Heroes" (ToH) application.
|
||||
This version gets some heroes from the server, displays them in a list, lets us add new heroes, and saves them to the server.
|
||||
|
||||
We use the !{_Angular_Http} client to communicate via `XMLHttpRequest (XHR)`.
|
||||
|
||||
It works like this.
|
||||
figure.image-display
|
||||
img(src='/resources/images/devguide/server-communication/http-toh.gif' alt="ToH mini app" width="250")
|
||||
:marked
|
||||
It's implemented with two components — a parent `TohComponent` shell and the `HeroListComponent` child.
|
||||
We've seen these kinds of component in many other documentation samples.
|
||||
Let's see how they change to support communication with a server.
|
||||
.l-sub-section
|
||||
:marked
|
||||
We're overdoing the "separation of concerns" by creating two components for a tiny demo.
|
||||
We're making a point about application structure that is easier to justify when the app grows.
|
||||
: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`,
|
||||
an array of service providers from the Angular HTTP library.
|
||||
We'll be using that library to access the server.
|
||||
We also import a `HeroService` that we'll look at shortly.
|
||||
|
||||
The component specifies both the ``HTTP_PROVIDERS` and the `HeroService` in the metadata `providers` array,
|
||||
making them available to the child components of this "Tour of Heroes" application.
|
||||
|
||||
.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
|
||||
Learn about providers in the [Dependency Injection](dependency-injection.html) chapter.
|
||||
|
||||
:marked
|
||||
This sample only has one child, the `HeroListComponent`. Here's its template:
|
||||
This demo has a single component, the `HeroListComponent`. Here's its template:
|
||||
+makeExample('server-communication/ts/app/toh/hero-list.component.html', null, 'app/toh/hero-list.component.html (template)')
|
||||
:marked
|
||||
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
|
||||
It presents the list of heroes with an `ngFor`.
|
||||
Below the list is an input box and an *Add Hero* button where we can enter the names of new heroes
|
||||
and add them to the database.
|
||||
We use a [template reference variable](template-syntax.html#ref-vars), `newHeroName`, to access the
|
||||
value of the input box in the `(click)` event binding.
|
||||
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 it ready for a new hero name.
|
||||
|
||||
Below the button is an area for an error message.
|
||||
|
||||
a(id="oninit")
|
||||
a(id="HeroListComponent")
|
||||
a#oninit
|
||||
a#HeroListComponent
|
||||
: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.
|
||||
That's the instance of the `HeroService` that we provided in the parent shell `TohComponent`.
|
||||
Angular [injects](dependency-injection.html) a `HeroService` into the constructor
|
||||
and the component calls that service to fetch and save data.
|
||||
|
||||
Notice that the component **does not talk to the server directly!**
|
||||
The component **does not talk directly to the !{_Angular_Http} client**!
|
||||
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).
|
||||
It delegates to the `HeroService`.
|
||||
|
||||
This is a golden rule: **always delegate data access to a supporting service class**.
|
||||
|
||||
|
@ -117,40 +152,39 @@ a(id="HeroListComponent")
|
|||
and count on Angular to call `ngOnInit` when it instantiates this component.
|
||||
.l-sub-section
|
||||
:marked
|
||||
This is a "best practice".
|
||||
This is a *best practice*.
|
||||
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.
|
||||
block getheroes-and-addhero
|
||||
:marked
|
||||
The service's `getHeroes()` and `addHero()` methods return an `Observable` of HTTP hero data.
|
||||
We subscribe to this `Observable`,
|
||||
specifying the actions to take when the request succeeds or fails.
|
||||
We'll get to observables and subscription shortly.
|
||||
The service's `getHeroes()` and `addHero()` methods return an `Observable` of hero data that the !{_Angular_Http} client fetched from the server.
|
||||
|
||||
*Observables* are a big topic, beyond the scope of this chapter.
|
||||
But we need to know a little about them to appreciate what is going on here.
|
||||
|
||||
We should think of an `Observable` as a stream of events published by some source.
|
||||
We listen for events in this stream by ***subscribing*** to the `Observable`.
|
||||
In these subscriptions we specify the actions to take when the web request
|
||||
produces a success event (with the hero data in the event payload) or a fail event (with the error in the payload).
|
||||
|
||||
:marked
|
||||
With our basic intuitions about the component squared away, we can turn to development of the backend data source
|
||||
and the client-side `HeroService` that talks to it.
|
||||
|
||||
### Fetch data
|
||||
|
||||
With our basic intuitions about the component squared away, we're ready to look inside the `HeroService`.
|
||||
|
||||
.l-main-section
|
||||
a#HeroService
|
||||
h2#fetch-data Fetch data with the #[b HeroService]
|
||||
:marked
|
||||
In many of our previous samples we faked the interaction with the server by
|
||||
returning mock heroes in a service like this one:
|
||||
+makeExample('toh-4/ts/app/hero.service.ts', 'just-get-heroes')(format=".")
|
||||
:marked
|
||||
In this chapter, we get the heroes from the server using a (browser-based) HTTP client service.
|
||||
Here's the new `HeroService`:
|
||||
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'v1', 'app/toh/hero.service.ts')
|
||||
|
||||
block http-client-service
|
||||
:marked
|
||||
The imported `Http` client service gets
|
||||
[injected](dependency-injection.html) into the `HeroService` constructor.
|
||||
.l-sub-section
|
||||
:marked
|
||||
`Http` is not part of the Angular core. It's an optional service in its own `@angular/http` library
|
||||
that we installed with npm (see the `package.json`) and
|
||||
registered for module loading by SystemJS (see `systemjs.config.js`)
|
||||
In this chapter, we revise that `HeroService` to get the heroes from the server using the !{_Angular_Http} client service:
|
||||
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'v1', 'app/toh/hero.service.ts (revised)')
|
||||
|
||||
:marked
|
||||
Notice that the Angular `!{_Http}` client service is
|
||||
[injected](dependency-injection.html) into the `HeroService` constructor.
|
||||
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'ctor')
|
||||
:marked
|
||||
Look closely at how we call `#{_priv}http.get`
|
||||
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'http-get', 'app/toh/hero.service.ts (getHeroes)')(format=".")
|
||||
|
@ -166,7 +200,8 @@ block http-client-service
|
|||
block rxjs
|
||||
:marked
|
||||
<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 who are familiar with asynchronous methods in modern JavaScript would expect the `get` method to return a
|
||||
[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.
|
||||
Instead we're calling a `map()` method.
|
||||
|
@ -177,7 +212,7 @@ block rxjs
|
|||
|
||||
.l-main-section
|
||||
:marked
|
||||
### RxJS Library
|
||||
# RxJS Library
|
||||
[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.
|
||||
|
||||
|
@ -201,22 +236,25 @@ block rxjs
|
|||
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
|
||||
Instead, we'll import each `Observable` operator and static class method, 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=".")
|
||||
+makeExample('server-communication/ts/app/rxjs-operators.ts', null, 'app/rxjs-operators.ts')(format=".")
|
||||
:marked
|
||||
If we forget an operator, the compiler will warn that it's missing and we'll update this file.
|
||||
If we forget an operator, the TypeScript 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).
|
||||
We don't need _all_ of these particular operators in the `HeroService` — just `map`, `catch` and `throw`.
|
||||
We'll need the other operators 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=".")
|
||||
|
||||
Finally, we import `rxjs-operator`_itself_ in our `app.component.ts`:
|
||||
+makeExample('server-communication/ts/app/app.component.ts', 'import-rxjs', 'app/app.component.ts (import rxjs)')(format=".")
|
||||
:marked
|
||||
Let's return to our study of the `HeroService`.
|
||||
|
||||
l-main-section
|
||||
a#extract-data
|
||||
:marked
|
||||
### Process the response object
|
||||
## Process the response object
|
||||
Remember that our `getHeroes()` method mapped the `#{_priv}http.get` response object to heroes with an `#{_priv}extractData` helper method:
|
||||
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'extract-data', 'app/toh/hero.service.ts (excerpt)')(format=".")
|
||||
:marked
|
||||
|
@ -293,8 +331,8 @@ h4 #[b HeroListComponent] error handling
|
|||
block hlc-error-handling
|
||||
:marked
|
||||
Back in the `HeroListComponent`, where we called `#{_priv}heroService.getHeroes()`,
|
||||
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.
|
||||
we supply the `subscribe` function with a second function parameter to handle the error message.
|
||||
It sets an `errorMessage` variable which we've bound conditionally in the `HeroListComponent` template.
|
||||
|
||||
+makeExample('server-communication/ts/app/toh/hero-list.component.ts', 'getHeroes', 'app/toh/hero-list.component.ts (getHeroes)')(format=".")
|
||||
|
||||
|
@ -370,8 +408,8 @@ code-example(format="." language="javascript").
|
|||
block hero-list-comp-add-hero
|
||||
:marked
|
||||
Back in the `HeroListComponent`, we see that *its* `addHero()` method subscribes to the observable returned by the *service's* `addHero()` method.
|
||||
When the data arrive it pushes the new hero object into its `heroes` array for presentation to the user.
|
||||
+makeExample('server-communication/ts/app/toh/hero-list.component.ts', 'addHero', 'app/toh/hero-list.component.ts (addHero)')(format=".")
|
||||
When the data, arrive it pushes the new hero object into its `heroes` array for presentation to the user.
|
||||
+makeExample('server-communication/ts/app/toh/hero-list.component.ts', 'addHero', 'app/toh/hero-list.component.ts (addHero)')(format=".")
|
||||
|
||||
block promises
|
||||
a#promises
|
||||
|
@ -388,9 +426,9 @@ block promises
|
|||
:marked
|
||||
Let's rewrite the `HeroService` using promises , highlighting just the parts that are different.
|
||||
+makeTabs(
|
||||
'server-communication/ts/app/toh/hero.service.1.ts,server-communication/ts/app/toh/hero.service.ts',
|
||||
'server-communication/ts/app/toh/hero.service.promise.ts,server-communication/ts/app/toh/hero.service.ts',
|
||||
'methods, methods',
|
||||
'app/toh/hero.service.ts (promise-based), app/toh/hero.service.ts (observable-based)')
|
||||
'app/toh/hero.service.promise.ts (promise-based), app/toh/hero.service.ts (observable-based)')
|
||||
:marked
|
||||
Converting from an observable to a promise is as simple as calling `toPromise(success, fail)`.
|
||||
|
||||
|
@ -405,9 +443,9 @@ block promises
|
|||
We have to adjust the calling component to expect a `Promise` instead of an `Observable`.
|
||||
|
||||
+makeTabs(
|
||||
'server-communication/ts/app/toh/hero-list.component.1.ts, server-communication/ts/app/toh/hero-list.component.ts',
|
||||
'server-communication/ts/app/toh/hero-list.component.promise.ts, server-communication/ts/app/toh/hero-list.component.ts',
|
||||
'methods, methods',
|
||||
'app/toh/hero-list.component.ts (promise-based), app/toh/hero-list.component.ts (observable-based)')
|
||||
'app/toh/hero-list.component.promise.ts (promise-based), app/toh/hero-list.component.ts (observable-based)')
|
||||
:marked
|
||||
The only obvious difference is that we call `then` on the returned promise instead of `subscribe`.
|
||||
We give both methods the same functional arguments.
|
||||
|
@ -457,7 +495,7 @@ figure.image-display
|
|||
|
||||
block wikipedia-jsonp+
|
||||
:marked
|
||||
Wikipedia offers both `CORS` and `JSONP` search APIs, let's use the latter for this example.
|
||||
Wikipedia offers a modern `CORS` API and a legacy `JSONP` search API. Let's use the latter for this example.
|
||||
The Angular `Jsonp` service both extends the `Http` service for JSONP and restricts us to `GET` requests.
|
||||
All other HTTP methods throw an error because JSONP is a read-only facility.
|
||||
|
||||
|
@ -637,7 +675,7 @@ a#in-mem-web-api
|
|||
method that returns a map whose keys are collection names and whose values
|
||||
are #{_array}s of objects in those collections.
|
||||
|
||||
Here's the class we created for this sample by copy-and-pasting the JSON data:
|
||||
Here's the class we created for this sample based on the JSON data:
|
||||
+makeExample('server-communication/ts/app/hero-data.ts', null, 'app/hero-data.ts')(format=".")
|
||||
:marked
|
||||
Ensure that the `HeroService` endpoint refers to the web API:
|
||||
|
@ -650,13 +688,10 @@ block redirect-to-web-api
|
|||
to a helper service called the `XHRBackend`.
|
||||
|
||||
To enable our server simulation, we replace the default `XHRBackend` service with
|
||||
the in-memory web API service using standard Angular provider registration
|
||||
in `TohComponent`. We initialize the in-memory web API with mock hero data at the same time.
|
||||
the in-memory web API service using standard Angular provider registration techniques.
|
||||
We initialize the in-memory web API with *seed data* from the mock hero dataset at the same time.
|
||||
|
||||
Here are the pertinent details, excerpt from `TohComponent`, starting with the imports:
|
||||
+makeExample('server-communication/ts/app/toh/toh.component.ts', 'in-mem-web-api-imports', 'toh.component.ts (web API imports)')(format=".")
|
||||
:marked
|
||||
Then we add the following two provider definitions to the `providers` array in component metadata:
|
||||
+makeExample('server-communication/ts/app/toh/toh.component.ts', 'in-mem-web-api-providers', 'toh.component.ts (web API providers)')(format=".")
|
||||
p Here is the revised (and final) version of the #[code #[+adjExPath('app/main.ts')]] demonstrating these steps.
|
||||
+makeExample('server-communication/ts/app/main.ts', 'final', 'app/main.ts (final)')(format=".")
|
||||
|
||||
p See the full source code in the #[+liveExampleLink2('live example', 'server-communication')].
|
||||
|
|
Loading…
Reference in New Issue