| @@ -2226,6 +2226,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", | |||
| @@ -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" | |||
| @@ -1,13 +1,16 @@ | |||
| import { BrowserModule } from '@angular/platform-browser'; | |||
| import { NgModule } from '@angular/core'; | |||
| import {BrowserModule} from '@angular/platform-browser'; | |||
| import {NgModule} from '@angular/core'; | |||
| import { AppComponent } from './app.component'; | |||
| import { HeroesComponent } from './heroes/heroes.component'; | |||
| import {AppComponent} from './app.component'; | |||
| import {HeroesComponent} from './heroes/heroes.component'; | |||
| import {FormsModule} from '@angular/forms'; | |||
| import { HeroDetailComponent } from './hero-detail/hero-detail.component'; | |||
| import { MessagesComponent } from './messages/messages.component'; | |||
| import { AppRoutingModule } from './app-routing.module'; | |||
| import { DashboardComponent } from './dashboard/dashboard.component'; | |||
| import {HeroDetailComponent} from './hero-detail/hero-detail.component'; | |||
| import {MessagesComponent} from './messages/messages.component'; | |||
| import {AppRoutingModule} from './app-routing.module'; | |||
| import {DashboardComponent} from './dashboard/dashboard.component'; | |||
| import {HttpClientModule} from "@angular/common/http"; | |||
| import {HttpClientInMemoryWebApiModule} from "angular-in-memory-web-api"; | |||
| import {InMemoryDataService} from "./in-memory-data.service"; | |||
| @NgModule({ | |||
| declarations: [ | |||
| @@ -20,9 +23,14 @@ import { DashboardComponent } from './dashboard/dashboard.component'; | |||
| imports: [ | |||
| BrowserModule, | |||
| FormsModule, | |||
| AppRoutingModule | |||
| AppRoutingModule, | |||
| HttpClientModule, | |||
| HttpClientInMemoryWebApiModule.forRoot( | |||
| InMemoryDataService, {dataEncapsulation: false} | |||
| ) | |||
| ], | |||
| providers: [], | |||
| bootstrap: [AppComponent] | |||
| }) | |||
| export class AppModule { } | |||
| export class AppModule { | |||
| } | |||
| @@ -8,3 +8,4 @@ | |||
| </div> | |||
| </div> | |||
| <button (click)="goBack()">go back</button> | |||
| <button (click)="save()">save</button> | |||
| @@ -36,4 +36,9 @@ export class HeroDetailComponent implements OnInit { | |||
| goBack() { | |||
| this.location.back(); | |||
| } | |||
| save() { | |||
| this.heroService.udpateHero(this.hero) | |||
| .subscribe(() => this.goBack()); | |||
| } | |||
| } | |||
| @@ -1,24 +1,79 @@ | |||
| import { Injectable } from '@angular/core'; | |||
| 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 {Hero} from './hero'; | |||
| import {Observable, of} from 'rxjs'; | |||
| import {MessageService} from './message.service'; | |||
| import {HttpClient, HttpHeaders} from "@angular/common/http"; | |||
| import {catchError, tap} from "rxjs/operators"; | |||
| @Injectable({ | |||
| providedIn: 'root' | |||
| }) | |||
| export class HeroService { | |||
| constructor(private messageService: MessageService) { } | |||
| private heroesUrl = 'api/heroes'; | |||
| httpOptions = { | |||
| headers: new HttpHeaders({ 'Content-Type': 'application/json' }) | |||
| }; | |||
| constructor( | |||
| private http: HttpClient, | |||
| private messageService: MessageService) { | |||
| } | |||
| getHeroes(): Observable<Hero[]> { | |||
| this.messageService.add('HeroService: fetched heroes') | |||
| return of(HEROES); | |||
| return this.http.get<Hero[]>(this.heroesUrl) | |||
| .pipe( | |||
| tap(_ => this.log('feteched heroes')), | |||
| catchError(this.handleError<Hero[]>('getHeroes', [])) | |||
| ); | |||
| } | |||
| getHero(id: number): Observable<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<Hero>(url).pipe( | |||
| tap(_ => this.log(`fetched hero id=${id}`)), | |||
| catchError(this.handleError<Hero>(`getHero id=${id}`)) | |||
| ); | |||
| } | |||
| addHero (hero: Hero): Observable<Hero> { | |||
| return this.http.post<Hero>(this.heroesUrl, hero, this.httpOptions).pipe( | |||
| tap((newHero: Hero) => this.log(`added hero w/ id=${newHero.id}`)), | |||
| catchError(this.handleError<Hero>('addHero')) | |||
| ); | |||
| } | |||
| 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, this.httpOptions).pipe( | |||
| tap(_ => this.log(`deleted hero id=${id}`)), | |||
| catchError(this.handleError<Hero>('deleteHero')) | |||
| ); | |||
| } | |||
| private log(message: string) { | |||
| this.messageService.add(`HeroService: ${message}`); | |||
| } | |||
| handleError<T>(operation: string = 'operation', result?: T) { | |||
| return (error: any): Observable<T> => { | |||
| console.error(error); | |||
| this.log(`${operation} failed: ${error.message}`); | |||
| return of(result as T); | |||
| } | |||
| } | |||
| udpateHero(hero: Hero): Observable<any> { | |||
| return this.http.put(this.heroesUrl, hero, this.httpOptions).pipe( | |||
| tap(_ => this.log(`updated hero id=${hero.id}`)), | |||
| catchError(this.handleError<any>('updateHero')) | |||
| ); | |||
| } | |||
| } | |||
| @@ -49,3 +49,25 @@ | |||
| margin-right: .8em; | |||
| border-radius: 4px 0 0 4px; | |||
| } | |||
| button { | |||
| background-color: #eee; | |||
| border: none; | |||
| padding: 5px 10px; | |||
| border-radius: 4px; | |||
| cursor: pointer; | |||
| cursor: hand; | |||
| font-family: Arial; | |||
| } | |||
| button:hover { | |||
| background-color: #cfd8dc; | |||
| } | |||
| button.delete { | |||
| position: relative; | |||
| left: 194px; | |||
| top: -32px; | |||
| background-color: gray !important; | |||
| color: white; | |||
| } | |||
| @@ -1,8 +1,19 @@ | |||
| <h2>My Heroes</h2> | |||
| <div> | |||
| <label>Hero name: | |||
| <input #heroName /> | |||
| </label> | |||
| <!-- (click) passes input value to add() and then clears the input --> | |||
| <button (click)="add(heroName.value); heroName.value=''"> | |||
| add | |||
| </button> | |||
| </div> | |||
| <ul class="heroes"> | |||
| <li *ngFor="let hero of heroes"> | |||
| <a routerLink="/detail/{{hero.id}}"> | |||
| <span class="badge">{{hero.id}}</span> {{hero.name}} | |||
| </a> | |||
| <button class="delete" title="delete hero" | |||
| (click)="delete(hero)">x</button> | |||
| </li> | |||
| </ul> | |||
| @@ -22,4 +22,18 @@ export class HeroesComponent implements OnInit { | |||
| this.heroService.getHeroes() | |||
| .subscribe(heroes => this.heroes = heroes); | |||
| } | |||
| add(name: string): void { | |||
| name = name.trim(); | |||
| if (!name) { return; } | |||
| this.heroService.addHero({ name } as Hero) | |||
| .subscribe(hero => { | |||
| this.heroes.push(hero); | |||
| }); | |||
| } | |||
| delete(hero: Hero): void { | |||
| this.heroes = this.heroes.filter(h => h !== hero); | |||
| this.heroService.deleteHero(hero).subscribe(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,32 @@ | |||
| import { Injectable } from '@angular/core'; | |||
| import {InMemoryDbService} from "angular-in-memory-web-api"; | |||
| import {Observable} from "rxjs"; | |||
| import {Hero} from "./hero"; | |||
| @Injectable({ | |||
| providedIn: 'root' | |||
| }) | |||
| export class InMemoryDataService implements InMemoryDbService { | |||
| constructor() { } | |||
| 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}; | |||
| } | |||
| genId(heroes: Hero[]): number { | |||
| return heroes.length > 0 ? Math.max(...heroes.map(hero => hero.id)) + 1 : 11; | |||
| } | |||
| } | |||