From 81405d7daa2503eca777b27b44f9d56919a4674e Mon Sep 17 00:00:00 2001 From: Olivier DOSSMANN Date: Fri, 25 Oct 2019 20:26:04 +0200 Subject: [PATCH] Angular Tour of Heroes: WIP sur HTTP --- angular-tour-of-heroes/package-lock.json | 5 ++ angular-tour-of-heroes/package.json | 1 + angular-tour-of-heroes/src/app/app.module.ts | 10 +++- .../src/app/hero.service.ts | 49 ++++++++++++++++--- .../src/app/heroes/heroes.component.ts | 1 - .../src/app/in-memory-data.service.spec.ts | 12 +++++ .../src/app/in-memory-data.service.ts | 33 +++++++++++++ angular-tour-of-heroes/src/app/mock-heroes.ts | 14 ------ 8 files changed, 101 insertions(+), 24 deletions(-) create mode 100644 angular-tour-of-heroes/src/app/in-memory-data.service.spec.ts create mode 100644 angular-tour-of-heroes/src/app/in-memory-data.service.ts delete mode 100644 angular-tour-of-heroes/src/app/mock-heroes.ts diff --git a/angular-tour-of-heroes/package-lock.json b/angular-tour-of-heroes/package-lock.json index f40e053..3743f2c 100644 --- a/angular-tour-of-heroes/package-lock.json +++ b/angular-tour-of-heroes/package-lock.json @@ -2235,6 +2235,11 @@ "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", "dev": true }, + "angular-in-memory-web-api": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/angular-in-memory-web-api/-/angular-in-memory-web-api-0.9.0.tgz", + "integrity": "sha512-//PiJ5qb1+Yf/N7270ioQqR2laf4/Irjavg+M+WEn8y4At9LUoYgbQ5HVwvM5xUTlVlL0XkbJRLxREcGGNdIEw==" + }, "ansi-colors": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", diff --git a/angular-tour-of-heroes/package.json b/angular-tour-of-heroes/package.json index 1b146d2..d436de1 100644 --- a/angular-tour-of-heroes/package.json +++ b/angular-tour-of-heroes/package.json @@ -19,6 +19,7 @@ "@angular/platform-browser": "~8.2.9", "@angular/platform-browser-dynamic": "~8.2.9", "@angular/router": "~8.2.9", + "angular-in-memory-web-api": "^0.9.0", "rxjs": "~6.4.0", "tslib": "^1.10.0", "zone.js": "~0.9.1" diff --git a/angular-tour-of-heroes/src/app/app.module.ts b/angular-tour-of-heroes/src/app/app.module.ts index 80b1c06..011092a 100644 --- a/angular-tour-of-heroes/src/app/app.module.ts +++ b/angular-tour-of-heroes/src/app/app.module.ts @@ -1,6 +1,7 @@ import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; // <-- NgModel lives here +import { HttpClientModule } from '@angular/common/http'; import { AppComponent } from './app.component'; import { HeroesComponent } from './heroes/heroes.component'; @@ -9,6 +10,9 @@ import { MessagesComponent } from './messages/messages.component'; import { AppRoutingModule } from './app-routing.module'; import { DashboardComponent } from './dashboard/dashboard.component'; +import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api'; +import { InMemoryDataService } from './in-memory-data.service'; + @NgModule({ declarations: [ AppComponent, @@ -20,7 +24,11 @@ import { DashboardComponent } from './dashboard/dashboard.component'; imports: [ BrowserModule, FormsModule, - AppRoutingModule + AppRoutingModule, + HttpClientModule, + HttpClientInMemoryWebApiModule.forRoot( + InMemoryDataService, { dataEncapsulation: false } + ) ], providers: [], bootstrap: [AppComponent] diff --git a/angular-tour-of-heroes/src/app/hero.service.ts b/angular-tour-of-heroes/src/app/hero.service.ts index 20fc51c..431085d 100644 --- a/angular-tour-of-heroes/src/app/hero.service.ts +++ b/angular-tour-of-heroes/src/app/hero.service.ts @@ -1,25 +1,58 @@ import { Injectable } from '@angular/core'; import { Hero } from './hero'; -import { HEROES } from './mock-heroes'; import { Observable, of } from 'rxjs'; import { MessageService } from './message.service'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { catchError, map, tap } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class HeroService { - constructor(private messageService: MessageService) { } + constructor( + private http: HttpClient, + private messageService: MessageService) { } + + private log(message: string) { + this.messageService.add(`HeroService: ${message}`) + } + + private heroesUrl = 'api/heroes'; // URL to web api getHeroes(): Observable { - // TODO: send the message _after_ fetching the heroes - this.messageService.add('HeroService: fetched heroes.') - return of(HEROES); + return this.http.get(this.heroesUrl) + .pipe( + catchError(this.handleError('getHeroes', [])) + ); } + /** GET hero by id. Will 404 if id not found */ getHero(id: number): Observable { - // TODO: send the message _after_ fetching the hero - this.messageService.add(`HeroService: fetched hero id=${id}`); - return of(HEROES.find(hero => hero.id === id)); + const url = `${this.heroesUrl}/${id}`; + return this.http.get(url).pipe( + tap(_ => this.log(`fetched hero id=${id}`)), + catchError(this.handleError(`getHero id=${id}`)) + ); + } + + /** + * 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); + }; } } diff --git a/angular-tour-of-heroes/src/app/heroes/heroes.component.ts b/angular-tour-of-heroes/src/app/heroes/heroes.component.ts index d678a02..cbd51b4 100644 --- a/angular-tour-of-heroes/src/app/heroes/heroes.component.ts +++ b/angular-tour-of-heroes/src/app/heroes/heroes.component.ts @@ -1,6 +1,5 @@ import { Component, OnInit } from '@angular/core'; import { Hero } from '../hero'; -import { HEROES } from '../mock-heroes'; import { HeroService } from '../hero.service'; @Component({ diff --git a/angular-tour-of-heroes/src/app/in-memory-data.service.spec.ts b/angular-tour-of-heroes/src/app/in-memory-data.service.spec.ts new file mode 100644 index 0000000..a75ef02 --- /dev/null +++ b/angular-tour-of-heroes/src/app/in-memory-data.service.spec.ts @@ -0,0 +1,12 @@ +import { TestBed } from '@angular/core/testing'; + +import { InMemoryDataService } from './in-memory-data.service'; + +describe('InMemoryDataService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: InMemoryDataService = TestBed.get(InMemoryDataService); + expect(service).toBeTruthy(); + }); +}); diff --git a/angular-tour-of-heroes/src/app/in-memory-data.service.ts b/angular-tour-of-heroes/src/app/in-memory-data.service.ts new file mode 100644 index 0000000..0c60689 --- /dev/null +++ b/angular-tour-of-heroes/src/app/in-memory-data.service.ts @@ -0,0 +1,33 @@ +import { InMemoryDbService } from 'angular-in-memory-web-api'; +import { Hero } from './hero'; +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root', +}) +export class InMemoryDataService implements InMemoryDbService { + createDb() { + const heroes = [ + { id: 11, name: 'Dr Nice' }, + { id: 12, name: 'Narco' }, + { id: 13, name: 'Bombasto' }, + { id: 14, name: 'Celeritas' }, + { id: 15, name: 'Magneta' }, + { id: 16, name: 'RubberMan' }, + { id: 17, name: 'Dynama' }, + { id: 18, name: 'Dr IQ' }, + { id: 19, name: 'Magma' }, + { id: 20, name: 'Tornado' } + ]; + return {heroes}; + } + + // Overrides the genId method to ensure that a hero always has an id. + // If the heroes array is empty, + // the method below returns the initial number (11). + // if the heroes array is not empty, the method below returns the highest + // hero id + 1. + genId(heroes: Hero[]): number { + return heroes.length > 0 ? Math.max(...heroes.map(hero => hero.id)) + 1 : 11; + } +} diff --git a/angular-tour-of-heroes/src/app/mock-heroes.ts b/angular-tour-of-heroes/src/app/mock-heroes.ts deleted file mode 100644 index bddb90c..0000000 --- a/angular-tour-of-heroes/src/app/mock-heroes.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Hero } from './hero'; - -export const HEROES: Hero[] = [ - { id: 11, name: 'Dr Nice' }, - { id: 12, name: 'Narco' }, - { id: 13, name: 'Bombasto' }, - { id: 14, name: 'Celeritas' }, - { id: 15, name: 'Magneta' }, - { id: 16, name: 'RubberMan' }, - { id: 17, name: 'Dynama' }, - { id: 18, name: 'Dr IQ' }, - { id: 19, name: 'Magma' }, - { id: 20, name: 'Tornado' } -]