Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// #docplaster
// #docregion
import { Component, OnInit } from '@angular/core';

// #docregion rxjs-imports
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import { of } from 'rxjs/observable/of';

import {
debounceTime, distinctUntilChanged, switchMap
} from 'rxjs/operators';
// #enddocregion rxjs-imports

import { Hero } from '../hero';
import { HeroService } from '../hero.service';

@Component({
selector: 'app-hero-search',
templateUrl: './hero-search.component.html',
styleUrls: [ './hero-search.component.css' ]
})
export class HeroSearchComponent implements OnInit {
// #docregion heroes-stream
heroes$: Observable<Hero[]>;
// #enddocregion heroes-stream
// #docregion searchTerms
private searchTerms = new Subject<string>();
// #enddocregion searchTerms

constructor(private heroService: HeroService) {}
// #docregion searchTerms

// 検索語をobservableストリームにpushする
search(term: string): void {
this.searchTerms.next(term);
}
// #enddocregion searchTerms

ngOnInit(): void {
// #docregion search
this.heroes$ = this.searchTerms.pipe(
// 各キーストロークの後、検索前に300ms待つ
debounceTime(300),

// 直前の検索語と同じ場合は無視する
distinctUntilChanged(),

// 検索語が変わる度に、新しい検索observableにスイッチする
switchMap((term: string) => this.heroService.searchHeroes(term)),
);
// #enddocregion search
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<h2>My Heroes</h2>

<!-- #docregion add -->
<div>
<label>Hero name:
<input #heroName />
</label>
<!-- (click) 入力値をadd()に渡したあと、入力をクリアする -->
<button (click)="add(heroName.value); heroName.value=''">
add
</button>
</div>
<!-- #enddocregion add -->

<!-- #docregion list -->
<ul class="heroes">
<li *ngFor="let hero of heroes">
<a routerLink="/detail/{{hero.id}}">
<span class="badge">{{hero.id}}</span> {{hero.name}}
</a>
<!-- #docregion delete -->
<button class="delete" title="delete hero"
(click)="delete(hero)">x</button>
<!-- #enddocregion delete -->
</li>
</ul>
<!-- #enddocregion list -->
158 changes: 158 additions & 0 deletions aio-ja/content/examples/toh-pt6/src/hero.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// #docplaster
// #docregion
import { Injectable } from '@angular/core';
// #docregion import-httpclient
import { HttpClient, HttpHeaders } from '@angular/common/http';
// #enddocregion import-httpclient

import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
// #docregion import-rxjs-operators
import { catchError, map, tap } from 'rxjs/operators';
// #enddocregion import-rxjs-operators

import { Hero } from './hero';
import { MessageService } from './message.service';

// #docregion http-options
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
// #enddocregion http-options

@Injectable()
export class HeroService {

// #docregion heroesUrl
private heroesUrl = 'api/heroes'; // Web APIのURL
// #enddocregion heroesUrl

// #docregion ctor
constructor(
private http: HttpClient,
private messageService: MessageService) { }
// #enddocregion ctor

// #docregion getHeroes, getHeroes-1
/** サーバーからヒーローを取得する */
// #docregion getHeroes-2
getHeroes (): Observable<Hero[]> {
return this.http.get<Hero[]>(this.heroesUrl)
// #enddocregion getHeroes-1
.pipe(
// #enddocregion getHeroes-2
tap(heroes => this.log(`fetched heroes`)),
// #docregion getHeroes-2
catchError(this.handleError('getHeroes', []))
);
// #docregion getHeroes-1
}
// #enddocregion getHeroes, getHeroes-1, getHeroes-2

// #docregion getHeroNo404
/** IDによりヒーローを取得. idが見つからない場合は`undefined`を返す */
getHeroNo404<Data>(id: number): Observable<Hero> {
const url = `${this.heroesUrl}/?id=${id}`;
return this.http.get<Hero[]>(url)
.pipe(
map(heroes => heroes[0]), // returns a {0|1} element array
// #enddocregion getHeroNo404
tap(h => {
const outcome = h ? `fetched` : `did not find`;
this.log(`${outcome} hero id=${id}`);
}),
catchError(this.handleError<Hero>(`getHero id=${id}`))
// #docregion getHeroNo404
);
}
// #enddocregion getHeroNo404

// #docregion getHero
/** IDによりヒーローを取得。見つからなかった場合は404を返却 */
getHero(id: number): Observable<Hero> {
const url = `${this.heroesUrl}/${id}`;
return this.http.get<Hero>(url).pipe(
tap(_ => this.log(`fetched hero id=${id}`)),
catchError(this.handleError<Hero>(`getHero id=${id}`))
);
}
// #enddocregion getHero

// #docregion searchHeroes
/* 検索語を含むヒーローを取得する */
searchHeroes(term: string): Observable<Hero[]> {
if (!term.trim()) {
// 検索語がない場合、空のヒーロー配列を返す
return of([]);
}
return this.http.get<Hero[]>(`api/heroes/?name=${term}`).pipe(
tap(_ => this.log(`found heroes matching "${term}"`)),
catchError(this.handleError<Hero[]>('searchHeroes', []))
);
}
// #enddocregion searchHeroes

//////// Save methods //////////

// #docregion addHero
/** POST: サーバーに新しいヒーローを登録する */
addHero (hero: Hero): Observable<Hero> {
return this.http.post<Hero>(this.heroesUrl, hero, httpOptions).pipe(
tap((hero: Hero) => this.log(`added hero w/ id=${hero.id}`)),
catchError(this.handleError<Hero>('addHero'))
);
}
// #enddocregion addHero

// #docregion deleteHero
/** DELETE: サーバーからヒーローを削除 */
deleteHero (hero: Hero | number): Observable<Hero> {
const id = typeof hero === 'number' ? hero : hero.id;
const url = `${this.heroesUrl}/${id}`;

return this.http.delete<Hero>(url, httpOptions).pipe(
tap(_ => this.log(`deleted hero id=${id}`)),
catchError(this.handleError<Hero>('deleteHero'))
);
}
// #enddocregion deleteHero

// #docregion updateHero
/** PUT: サーバー上でヒーローを更新 */
updateHero (hero: Hero): Observable<any> {
return this.http.put(this.heroesUrl, hero, httpOptions).pipe(
tap(_ => this.log(`updated hero id=${hero.id}`)),
catchError(this.handleError<any>('updateHero'))
);
}
// #enddocregion updateHero

// #docregion handleError
/**
* 失敗したHttp操作を処理します
* アプリを持続させます。
* @param operation - 失敗した操作の名前
* @param result - observableな結果として返す任意の値
*/
private handleError<T> (operation = 'operation', result?: T) {
return (error: any): Observable<T> => {

// TODO: リモート上のロギング基盤にエラーを送信する
console.error(error); // かわりにconsoleに出力

// TODO: ユーザーへの開示のためにエラーの変換処理を改善する
this.log(`${operation} failed: ${error.message}`);

// 空の結果を返して、アプリを持続可能にする
return of(result as T);
};
}
// #enddocregion handleError

// #docregion log
/** HeroServiceのメッセージをMessageServiceを使って記録 */
private log(message: string) {
this.messageService.add('HeroService: ' + message);
}
// #enddocregion log
}
Loading