Skip to content

Commit f1b5a46

Browse files
authored
Merge pull request #64 from chloe463/translate/toh-pt6
tutorial/toh-pt6.md の翻訳
2 parents 98981ea + b9c838a commit f1b5a46

File tree

4 files changed

+492
-285
lines changed

4 files changed

+492
-285
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// #docplaster
2+
// #docregion
3+
import { Component, OnInit } from '@angular/core';
4+
5+
// #docregion rxjs-imports
6+
import { Observable } from 'rxjs/Observable';
7+
import { Subject } from 'rxjs/Subject';
8+
import { of } from 'rxjs/observable/of';
9+
10+
import {
11+
debounceTime, distinctUntilChanged, switchMap
12+
} from 'rxjs/operators';
13+
// #enddocregion rxjs-imports
14+
15+
import { Hero } from '../hero';
16+
import { HeroService } from '../hero.service';
17+
18+
@Component({
19+
selector: 'app-hero-search',
20+
templateUrl: './hero-search.component.html',
21+
styleUrls: [ './hero-search.component.css' ]
22+
})
23+
export class HeroSearchComponent implements OnInit {
24+
// #docregion heroes-stream
25+
heroes$: Observable<Hero[]>;
26+
// #enddocregion heroes-stream
27+
// #docregion searchTerms
28+
private searchTerms = new Subject<string>();
29+
// #enddocregion searchTerms
30+
31+
constructor(private heroService: HeroService) {}
32+
// #docregion searchTerms
33+
34+
// 検索語をobservableストリームにpushする
35+
search(term: string): void {
36+
this.searchTerms.next(term);
37+
}
38+
// #enddocregion searchTerms
39+
40+
ngOnInit(): void {
41+
// #docregion search
42+
this.heroes$ = this.searchTerms.pipe(
43+
// 各キーストロークの後、検索前に300ms待つ
44+
debounceTime(300),
45+
46+
// 直前の検索語と同じ場合は無視する
47+
distinctUntilChanged(),
48+
49+
// 検索語が変わる度に、新しい検索observableにスイッチする
50+
switchMap((term: string) => this.heroService.searchHeroes(term)),
51+
);
52+
// #enddocregion search
53+
}
54+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<h2>My Heroes</h2>
2+
3+
<!-- #docregion add -->
4+
<div>
5+
<label>Hero name:
6+
<input #heroName />
7+
</label>
8+
<!-- (click) 入力値をadd()に渡したあと、入力をクリアする -->
9+
<button (click)="add(heroName.value); heroName.value=''">
10+
add
11+
</button>
12+
</div>
13+
<!-- #enddocregion add -->
14+
15+
<!-- #docregion list -->
16+
<ul class="heroes">
17+
<li *ngFor="let hero of heroes">
18+
<a routerLink="/detail/{{hero.id}}">
19+
<span class="badge">{{hero.id}}</span> {{hero.name}}
20+
</a>
21+
<!-- #docregion delete -->
22+
<button class="delete" title="delete hero"
23+
(click)="delete(hero)">x</button>
24+
<!-- #enddocregion delete -->
25+
</li>
26+
</ul>
27+
<!-- #enddocregion list -->
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
// #docplaster
2+
// #docregion
3+
import { Injectable } from '@angular/core';
4+
// #docregion import-httpclient
5+
import { HttpClient, HttpHeaders } from '@angular/common/http';
6+
// #enddocregion import-httpclient
7+
8+
import { Observable } from 'rxjs/Observable';
9+
import { of } from 'rxjs/observable/of';
10+
// #docregion import-rxjs-operators
11+
import { catchError, map, tap } from 'rxjs/operators';
12+
// #enddocregion import-rxjs-operators
13+
14+
import { Hero } from './hero';
15+
import { MessageService } from './message.service';
16+
17+
// #docregion http-options
18+
const httpOptions = {
19+
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
20+
};
21+
// #enddocregion http-options
22+
23+
@Injectable()
24+
export class HeroService {
25+
26+
// #docregion heroesUrl
27+
private heroesUrl = 'api/heroes'; // Web APIのURL
28+
// #enddocregion heroesUrl
29+
30+
// #docregion ctor
31+
constructor(
32+
private http: HttpClient,
33+
private messageService: MessageService) { }
34+
// #enddocregion ctor
35+
36+
// #docregion getHeroes, getHeroes-1
37+
/** サーバーからヒーローを取得する */
38+
// #docregion getHeroes-2
39+
getHeroes (): Observable<Hero[]> {
40+
return this.http.get<Hero[]>(this.heroesUrl)
41+
// #enddocregion getHeroes-1
42+
.pipe(
43+
// #enddocregion getHeroes-2
44+
tap(heroes => this.log(`fetched heroes`)),
45+
// #docregion getHeroes-2
46+
catchError(this.handleError('getHeroes', []))
47+
);
48+
// #docregion getHeroes-1
49+
}
50+
// #enddocregion getHeroes, getHeroes-1, getHeroes-2
51+
52+
// #docregion getHeroNo404
53+
/** IDによりヒーローを取得. idが見つからない場合は`undefined`を返す */
54+
getHeroNo404<Data>(id: number): Observable<Hero> {
55+
const url = `${this.heroesUrl}/?id=${id}`;
56+
return this.http.get<Hero[]>(url)
57+
.pipe(
58+
map(heroes => heroes[0]), // returns a {0|1} element array
59+
// #enddocregion getHeroNo404
60+
tap(h => {
61+
const outcome = h ? `fetched` : `did not find`;
62+
this.log(`${outcome} hero id=${id}`);
63+
}),
64+
catchError(this.handleError<Hero>(`getHero id=${id}`))
65+
// #docregion getHeroNo404
66+
);
67+
}
68+
// #enddocregion getHeroNo404
69+
70+
// #docregion getHero
71+
/** IDによりヒーローを取得。見つからなかった場合は404を返却 */
72+
getHero(id: number): Observable<Hero> {
73+
const url = `${this.heroesUrl}/${id}`;
74+
return this.http.get<Hero>(url).pipe(
75+
tap(_ => this.log(`fetched hero id=${id}`)),
76+
catchError(this.handleError<Hero>(`getHero id=${id}`))
77+
);
78+
}
79+
// #enddocregion getHero
80+
81+
// #docregion searchHeroes
82+
/* 検索語を含むヒーローを取得する */
83+
searchHeroes(term: string): Observable<Hero[]> {
84+
if (!term.trim()) {
85+
// 検索語がない場合、空のヒーロー配列を返す
86+
return of([]);
87+
}
88+
return this.http.get<Hero[]>(`api/heroes/?name=${term}`).pipe(
89+
tap(_ => this.log(`found heroes matching "${term}"`)),
90+
catchError(this.handleError<Hero[]>('searchHeroes', []))
91+
);
92+
}
93+
// #enddocregion searchHeroes
94+
95+
//////// Save methods //////////
96+
97+
// #docregion addHero
98+
/** POST: サーバーに新しいヒーローを登録する */
99+
addHero (hero: Hero): Observable<Hero> {
100+
return this.http.post<Hero>(this.heroesUrl, hero, httpOptions).pipe(
101+
tap((hero: Hero) => this.log(`added hero w/ id=${hero.id}`)),
102+
catchError(this.handleError<Hero>('addHero'))
103+
);
104+
}
105+
// #enddocregion addHero
106+
107+
// #docregion deleteHero
108+
/** DELETE: サーバーからヒーローを削除 */
109+
deleteHero (hero: Hero | number): Observable<Hero> {
110+
const id = typeof hero === 'number' ? hero : hero.id;
111+
const url = `${this.heroesUrl}/${id}`;
112+
113+
return this.http.delete<Hero>(url, httpOptions).pipe(
114+
tap(_ => this.log(`deleted hero id=${id}`)),
115+
catchError(this.handleError<Hero>('deleteHero'))
116+
);
117+
}
118+
// #enddocregion deleteHero
119+
120+
// #docregion updateHero
121+
/** PUT: サーバー上でヒーローを更新 */
122+
updateHero (hero: Hero): Observable<any> {
123+
return this.http.put(this.heroesUrl, hero, httpOptions).pipe(
124+
tap(_ => this.log(`updated hero id=${hero.id}`)),
125+
catchError(this.handleError<any>('updateHero'))
126+
);
127+
}
128+
// #enddocregion updateHero
129+
130+
// #docregion handleError
131+
/**
132+
* 失敗したHttp操作を処理します
133+
* アプリを持続させます。
134+
* @param operation - 失敗した操作の名前
135+
* @param result - observableな結果として返す任意の値
136+
*/
137+
private handleError<T> (operation = 'operation', result?: T) {
138+
return (error: any): Observable<T> => {
139+
140+
// TODO: リモート上のロギング基盤にエラーを送信する
141+
console.error(error); // かわりにconsoleに出力
142+
143+
// TODO: ユーザーへの開示のためにエラーの変換処理を改善する
144+
this.log(`${operation} failed: ${error.message}`);
145+
146+
// 空の結果を返して、アプリを持続可能にする
147+
return of(result as T);
148+
};
149+
}
150+
// #enddocregion handleError
151+
152+
// #docregion log
153+
/** HeroServiceのメッセージをMessageServiceを使って記録 */
154+
private log(message: string) {
155+
this.messageService.add('HeroService: ' + message);
156+
}
157+
// #enddocregion log
158+
}

0 commit comments

Comments
 (0)