Skip to content
This repository was archived by the owner on Dec 4, 2017. It is now read-only.

Commit 8c47c86

Browse files
committed
docs(toh-6/ts): refactoring of 'add, edit, delete heroes'
Refactoring of "add, edit, delete heroes" section of toh-6 from one big bottom-up step into small independent feature slices, where the user achieves a "milesone" (i.e., can run the full app) after each feature section. The section rewrite is shorter and offers a better UX. Other simplifications: - Error handling is consistent: in the hero service we log to the console, everwhere else we just let errors bubble up. - Hero service methods renamed based on function (create, update) rather then lower-level implementation (post, put). - @output properties have been eliminated (since they weren't explained). E2E tests now pass on both the TS and Dart sides. Post-Dart-review updates included.
1 parent 00ba1f9 commit 8c47c86

File tree

9 files changed

+419
-445
lines changed

9 files changed

+419
-445
lines changed

public/docs/_examples/toh-6/e2e-spec.ts

Lines changed: 234 additions & 197 deletions
Large diffs are not rendered by default.

public/docs/_examples/toh-6/ts/app/hero-detail.component.html

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
<!-- #docplaster -->
21
<!-- #docregion -->
32
<div *ngIf="hero">
43
<h2>{{hero.name}} details!</h2>
Lines changed: 11 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
// #docplaster
2-
// #docregion, variables-imports
3-
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
4-
5-
// #enddocregion variables-imports
1+
// #docregion
2+
import { Component, OnInit } from '@angular/core';
63
import { ActivatedRoute, Params } from '@angular/router';
74

85
import { Hero } from './hero';
@@ -13,50 +10,30 @@ import { HeroService } from './hero.service';
1310
templateUrl: 'app/hero-detail.component.html',
1411
styleUrls: ['app/hero-detail.component.css']
1512
})
16-
// #docregion variables-imports
1713
export class HeroDetailComponent implements OnInit {
18-
@Input() hero: Hero;
19-
@Output() close = new EventEmitter();
20-
error: any;
21-
navigated = false; // true if navigated here
22-
// #enddocregion variables-imports
14+
hero: Hero;
2315

2416
constructor(
2517
private heroService: HeroService,
2618
private route: ActivatedRoute) {
2719
}
2820

29-
// #docregion ngOnInit
3021
ngOnInit(): void {
3122
this.route.params.forEach((params: Params) => {
32-
if (params['id'] !== undefined) {
33-
let id = +params['id'];
34-
this.navigated = true;
35-
this.heroService.getHero(id)
36-
.then(hero => this.hero = hero);
37-
} else {
38-
this.navigated = false;
39-
this.hero = new Hero();
40-
}
23+
let id = +params['id'];
24+
this.heroService.getHero(id)
25+
.then(hero => this.hero = hero);
4126
});
4227
}
43-
// #enddocregion ngOnInit
4428

4529
// #docregion save
4630
save(): void {
47-
this.heroService
48-
.save(this.hero)
49-
.then(hero => {
50-
this.hero = hero; // saved hero, w/ id if new
51-
this.goBack(hero);
52-
})
53-
.catch(error => this.error = error); // TODO: Display error message
31+
this.heroService.update(this.hero)
32+
.then(this.goBack);
5433
}
5534
// #enddocregion save
56-
// #docregion goBack
57-
goBack(savedHero: Hero = null): void {
58-
this.close.emit(savedHero);
59-
if (this.navigated) { window.history.back(); }
35+
36+
goBack(): void {
37+
window.history.back();
6038
}
61-
// #enddocregion goBack
6239
}

public/docs/_examples/toh-6/ts/app/hero.service.ts

Lines changed: 28 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
// #docplaster
2-
// #docregion
2+
// #docregion , imports
33
import { Injectable } from '@angular/core';
4-
import { Headers, Http, Response } from '@angular/http';
4+
import { Headers, Http } from '@angular/http';
55

66
// #docregion rxjs
77
import 'rxjs/add/operator/toPromise';
88
// #enddocregion rxjs
99

1010
import { Hero } from './hero';
11+
// #enddocregion imports
1112

1213
@Injectable()
1314
export class HeroService {
1415

16+
// #docregion update
17+
private headers = new Headers({'Content-Type': 'application/json'});
18+
// #enddocregion update
1519
// #docregion getHeroes
1620
private heroesUrl = 'app/heroes'; // URL to web api
1721

@@ -36,62 +40,40 @@ export class HeroService {
3640
.then(heroes => heroes.find(hero => hero.id === id));
3741
}
3842

39-
// #docregion save
40-
save(hero: Hero): Promise<Hero> {
41-
if (hero.id) {
42-
return this.put(hero);
43-
}
44-
return this.post(hero);
45-
}
46-
// #enddocregion save
47-
4843
// #docregion delete
49-
delete(hero: Hero): Promise<Response> {
50-
let headers = new Headers();
51-
headers.append('Content-Type', 'application/json');
52-
53-
let url = `${this.heroesUrl}/${hero.id}`;
54-
55-
return this.http
56-
.delete(url, {headers: headers})
57-
.toPromise()
58-
.catch(this.handleError);
44+
delete(id: number): Promise<void> {
45+
let url = `${this.heroesUrl}/${id}`;
46+
return this.http.delete(url, {headers: this.headers})
47+
.toPromise()
48+
.then(() => null)
49+
.catch(this.handleError);
5950
}
6051
// #enddocregion delete
6152

62-
// #docregion post
63-
// Add new Hero
64-
private post(hero: Hero): Promise<Hero> {
65-
let headers = new Headers({
66-
'Content-Type': 'application/json'});
67-
53+
// #docregion create
54+
create(name: string): Promise<Hero> {
6855
return this.http
69-
.post(this.heroesUrl, JSON.stringify(hero), {headers: headers})
70-
.toPromise()
71-
.then(res => res.json().data)
72-
.catch(this.handleError);
56+
.post(this.heroesUrl, JSON.stringify({name: name}), {headers: this.headers})
57+
.toPromise()
58+
.then(res => res.json().data)
59+
.catch(this.handleError);
7360
}
74-
// #enddocregion post
75-
76-
// #docregion put
77-
// Update existing Hero
78-
private put(hero: Hero): Promise<Hero> {
79-
let headers = new Headers();
80-
headers.append('Content-Type', 'application/json');
81-
82-
let url = `${this.heroesUrl}/${hero.id}`;
61+
// #enddocregion create
62+
// #docregion update
8363

64+
update(hero: Hero): Promise<Hero> {
65+
const url = `${this.heroesUrl}/${hero.id}`;
8466
return this.http
85-
.put(url, JSON.stringify(hero), {headers: headers})
86-
.toPromise()
87-
.then(() => hero)
88-
.catch(this.handleError);
67+
.put(url, JSON.stringify(hero), {headers: this.headers})
68+
.toPromise()
69+
.then(() => hero)
70+
.catch(this.handleError);
8971
}
90-
// #enddocregion put
72+
// #enddocregion put, update
9173

9274
// #docregion handleError
9375
private handleError(error: any): Promise<any> {
94-
console.error('An error occurred', error);
76+
console.error('An error occurred', error); // for demo purposes only
9577
return Promise.reject(error.message || error);
9678
}
9779
// #enddocregion handleError

public/docs/_examples/toh-6/ts/app/heroes.component.css

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,10 @@ button:hover {
5959
background-color: #cfd8dc;
6060
}
6161
/* #docregion additions */
62-
.error {color:red;}
63-
button.delete-button{
62+
button.delete {
6463
float:right;
64+
margin-top: 2px;
65+
margin-right: .8em;
6566
background-color: gray !important;
6667
color:white;
6768
}

public/docs/_examples/toh-6/ts/app/heroes.component.ts

Lines changed: 19 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,65 +4,55 @@ import { Router } from '@angular/router';
44

55
import { Hero } from './hero';
66
import { HeroService } from './hero.service';
7-
// #docregion hero-detail-component
87

98
@Component({
109
selector: 'my-heroes',
1110
templateUrl: 'app/heroes.component.html',
1211
styleUrls: ['app/heroes.component.css']
1312
})
14-
// #enddocregion hero-detail-component
1513
export class HeroesComponent implements OnInit {
1614
heroes: Hero[];
1715
selectedHero: Hero;
18-
addingHero = false;
19-
// #docregion error
20-
error: any;
21-
// #enddocregion error
2216

2317
constructor(
24-
private router: Router,
25-
private heroService: HeroService) { }
18+
private heroService: HeroService,
19+
private router: Router) { }
2620

2721
getHeroes(): void {
2822
this.heroService
2923
.getHeroes()
30-
.then(heroes => this.heroes = heroes)
31-
.catch(error => this.error = error);
24+
.then(heroes => this.heroes = heroes);
3225
}
3326

34-
// #docregion addHero
35-
addHero(): void {
36-
this.addingHero = true;
37-
this.selectedHero = null;
27+
// #docregion add
28+
add(name: string): void {
29+
name = name.trim();
30+
if (!name) { return; }
31+
this.heroService.create(name)
32+
.then(hero => {
33+
this.heroes.push(hero);
34+
this.selectedHero = null;
35+
});
3836
}
37+
// #enddocregion add
3938

40-
close(savedHero: Hero): void {
41-
this.addingHero = false;
42-
if (savedHero) { this.getHeroes(); }
43-
}
44-
// #enddocregion addHero
45-
46-
// #docregion deleteHero
47-
deleteHero(hero: Hero, event: any): void {
48-
event.stopPropagation();
39+
// #docregion delete
40+
delete(hero: Hero): void {
4941
this.heroService
50-
.delete(hero)
51-
.then(res => {
42+
.delete(hero.id)
43+
.then(() => {
5244
this.heroes = this.heroes.filter(h => h !== hero);
5345
if (this.selectedHero === hero) { this.selectedHero = null; }
54-
})
55-
.catch(error => this.error = error);
46+
});
5647
}
57-
// #enddocregion deleteHero
48+
// #enddocregion delete
5849

5950
ngOnInit(): void {
6051
this.getHeroes();
6152
}
6253

6354
onSelect(hero: Hero): void {
6455
this.selectedHero = hero;
65-
this.addingHero = false;
6656
}
6757

6858
gotoDetail(): void {

public/docs/ts/_cache/tutorial/toh-pt6.jade

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,12 @@ block backend
9797
The `InMemoryWebApiModule` replaces the default `Http` client backend &mdash;
9898
the supporting service that talks to the remote server &mdash;
9999
with an _in-memory web API alternative service_.
100+
100101
+makeExcerpt(_appModuleTsVsMainTs, 'in-mem-web-api', '')
102+
101103
:marked
102104
The `forRoot` configuration method takes an `InMemoryDataService` class
103-
that will prime the in-memory database as follows:
105+
that primes the in-memory database as follows:
104106

105107
+makeExample('app/in-memory-data.service.ts', 'init')(format='.')
106108

@@ -126,7 +128,7 @@ block dont-be-distracted-by-backend-subst
126128

127129
:marked
128130
We returned a !{_Promise} resolved with mock heroes.
129-
It may have seemed like an overkill at the time, but we were anticipating the
131+
It may have seemed like overkill at the time, but we were anticipating the
130132
day when we fetched heroes with an HTTP client and we knew that would have to be an asynchronous operation.
131133

132134
That day has arrived! Let's convert `getHeroes()` to use HTTP.
@@ -140,7 +142,7 @@ block dont-be-distracted-by-backend-subst
140142

141143
- var _h3id = `http-${_promise}`
142144
:marked
143-
Refresh the browser and the hero data should be successfully loaded from the
145+
Refresh the browser, and the hero data should be successfully loaded from the
144146
mock server.
145147

146148
<h3 id="!{_h3id}">HTTP !{_Promise}</h3>
@@ -217,23 +219,21 @@ block get-heroes-details
217219
Although we made significant *internal* changes to `getHeroes()`, the public signature did not change.
218220
We still return a !{_Promise}. We won't have to update any of the components that call `getHeroes()`.
219221

220-
### Add, edit, delete heroes
222+
Our stakeholders are incredibly pleased with the added flexibility from the API integration, but it doesn't stop there. Next, we want the ability to create new heroes and delete heroes.
221223

222-
Our stakeholders are incredibly pleased with the added flexibility from the API integration, but it doesn't stop there. Next we want to add the capability to add, edit and delete heroes.
224+
But first, let's see what happens now when we try to update a hero's details.
223225

224-
We'll start with the edit feature since we have most of the capabilities already.
225-
226-
.l-main-section#edit
226+
.l-main-section
227227
:marked
228-
## Persist name changes to the server
228+
## Update hero details
229229

230230
The hero detail view already allows us to edit a hero's name. Go ahead, try
231231
it now. As we type, the hero name is updated in the view heading, but
232-
notice what happens when we hit the `Back` button: the edits are lost!
232+
notice what happens when we hit the `Back` button: the changes are lost!
233233

234234
.l-sub-section
235235
:marked
236-
Changes weren't lost before, what's happening?
236+
Updates weren't lost before, what's happening?
237237
When the app used a list of mock heroes, changes were made directly to the
238238
hero objects in the single, app-wide shared list. Now that we are fetching data
239239
from a server, if we want changes to persist, we'll need to write them back to
@@ -336,7 +336,7 @@ block get-heroes-details
336336
:marked
337337
### Hero service `delete` method
338338

339-
The delete HTTP method will be used to delete heroes and its invocation is like to `put` except for the method name:
339+
The hero service's `delete` method uses the _delete_ HTTP method to remove the hero from the server:
340340

341341
+makeExcerpt('app/hero.service.ts', 'delete')
342342

@@ -509,7 +509,8 @@ block observable-transformers
509509
- var _declarations = _docsFor == 'dart' ? 'directives' : 'declarations'
510510
- var declFile = _docsFor == 'dart' ? 'app/dashboard.component.ts' : 'app/app.module.ts'
511511
:marked
512-
And finally, we import the `HeroSearchComponent` from `'./hero-search.component.ts'`
512+
And finally, we import `HeroSearchComponent` from
513+
<span ngio-ex>hero-search.component.ts</span>
513514
and add it to the `!{_declarations}` !{_array}:
514515

515516
+makeExcerpt(declFile, 'search')

public/docs/ts/latest/tutorial/_data.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@
3131
"nextable": true
3232
},
3333
"toh-pt6": {
34-
"title": "Http",
35-
"intro": "We convert our service and components to use Http",
34+
"title": "HTTP",
35+
"intro": "We convert our service and components to use HTTP",
3636
"nextable": true
3737
}
38-
}
38+
}

0 commit comments

Comments
 (0)