// #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'; // URL to web api // #enddocregion heroesUrl // #docregion ctor constructor( private http: HttpClient, private messageService: MessageService) { } // #enddocregion ctor // #docregion getHeroes, getHeroes-1 /** GET heroes from the server */ // #docregion getHeroes-2 getHeroes (): Observable { return this.http.get(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 /** GET hero by id. Return `undefined` when id not found */ getHeroNo404(id: number): Observable { const url = `${this.heroesUrl}/?id=${id}`; return this.http.get(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(`getHero id=${id}`)) // #docregion getHeroNo404 ); } // #enddocregion getHeroNo404 // #docregion getHero /** GET hero by id. Will 404 if id not found */ getHero(id: number): Observable { const url = `${this.heroesUrl}/${id}`; return this.http.get(url).pipe( tap(_ => this.log(`fetched hero id=${id}`)), catchError(this.handleError(`getHero id=${id}`)) ); } // #enddocregion getHero // #docregion searchHeroes /* GET heroes whose name contains search term */ searchHeroes(term: string): Observable { if (!term.trim()) { // if not search term, return empty hero array. return of([]); } return this.http.get(`api/heroes/?name=${term}`).pipe( tap(_ => this.log(`found heroes matching "${term}"`)), catchError(this.handleError('searchHeroes', [])) ); } // #enddocregion searchHeroes //////// Save methods ////////// // #docregion addHero /** POST: add a new hero to the server */ addHero (hero: Hero): Observable { return this.http.post(this.heroesUrl, hero, httpOptions).pipe( tap((hero: Hero) => this.log(`added hero w/ id=${hero.id}`)), catchError(this.handleError('addHero')) ); } // #enddocregion addHero // #docregion deleteHero /** DELETE: delete the hero from the server */ deleteHero (hero: Hero | number): Observable { const id = typeof hero === 'number' ? hero : hero.id; const url = `${this.heroesUrl}/${id}`; return this.http.delete(url, httpOptions).pipe( tap(_ => this.log(`deleted hero id=${id}`)), catchError(this.handleError('deleteHero')) ); } // #enddocregion deleteHero // #docregion updateHero /** PUT: update the hero on the server */ updateHero (hero: Hero): Observable { return this.http.put(this.heroesUrl, hero, httpOptions).pipe( tap(_ => this.log(`updated hero id=${hero.id}`)), catchError(this.handleError('updateHero')) ); } // #enddocregion updateHero // #docregion handleError /** * Handle Http operation that failed. * Let the app continue. * @param operation - name of the operation that failed * @param result - optional value to return as the observable result */ private handleError (operation = 'operation', result?: T) { return (error: any): Observable => { // TODO: send the error to remote logging infrastructure console.error(error); // log to console instead // TODO: better job of transforming error for user consumption this.log(`${operation} failed: ${error.message}`); // Let the app keep running by returning an empty result. return of(result as T); }; } // #enddocregion handleError // #docregion log /** Log a HeroService message with the MessageService */ private log(message: string) { this.messageService.add('HeroService: ' + message); } // #enddocregion log }