diff --git a/package-lock.json b/package-lock.json
index f2cf6ce..49d16fc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -473,6 +473,11 @@
}
}
},
+ "angular-in-memory-web-api": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/angular-in-memory-web-api/-/angular-in-memory-web-api-0.5.3.tgz",
+ "integrity": "sha512-1QPwwXG8R/2s7EbHh13HDiJYsk4sdBHNxHJHZHJ/Kxb4T9OG+bb1kGcXzY9UrJkEVxOtUW0ozvL4p/HmeIEszg=="
+ },
"ansi-html": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz",
diff --git a/package.json b/package.json
index 4dfd7c1..31b1eac 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
"@angular/platform-browser": "^5.2.0",
"@angular/platform-browser-dynamic": "^5.2.0",
"@angular/router": "^5.2.0",
+ "angular-in-memory-web-api": "^0.5.3",
"core-js": "^2.4.1",
"rxjs": "^5.5.6",
"zone.js": "^0.8.19"
diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
new file mode 100644
index 0000000..1b2d9f4
--- /dev/null
+++ b/src/app/app-routing.module.ts
@@ -0,0 +1,18 @@
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+import { HeroesComponent } from './heroes/heroes.component';
+import { DashboardComponent } from './dashboard/dashboard.component';
+import { HeroDetailComponent } from './hero-detail/hero-detail.component';
+
+const routes: Routes = [
+ { path: 'heroes', component: HeroesComponent },
+ { path: 'dashboard', component: DashboardComponent },
+ { path: 'detail/:id', component: HeroDetailComponent },
+ { path: '', redirectTo: '/dashboard', pathMatch: 'full' }
+];
+
+@NgModule({
+ imports: [ RouterModule.forRoot(routes) ],
+ exports: [ RouterModule ]
+})
+export class AppRoutingModule { }
diff --git a/src/app/app.component.css b/src/app/app.component.css
index e69de29..bf741e4 100644
--- a/src/app/app.component.css
+++ b/src/app/app.component.css
@@ -0,0 +1,29 @@
+/* AppComponent's private CSS styles */
+h1 {
+ font-size: 1.2em;
+ color: #999;
+ margin-bottom: 0;
+}
+h2 {
+ font-size: 2em;
+ margin-top: 0;
+ padding-top: 0;
+}
+nav a {
+ padding: 5px 10px;
+ text-decoration: none;
+ margin-top: 10px;
+ display: inline-block;
+ background-color: #eee;
+ border-radius: 4px;
+}
+nav a:visited, a:link {
+ color: #607D8B;
+}
+nav a:hover {
+ color: #039be5;
+ background-color: #CFD8DC;
+}
+nav a.active {
+ color: #039be5;
+}
diff --git a/src/app/app.component.html b/src/app/app.component.html
index fa2706a..d5f2d75 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -1,20 +1,7 @@
-
-
-
- Welcome to {{ title }}!
-
-
-
-Here are some links to help you start:
-
-
+ {{ title }}
+
+
+
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 7b0f672..c41599e 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -6,5 +6,5 @@ import { Component } from '@angular/core';
styleUrls: ['./app.component.css']
})
export class AppComponent {
- title = 'app';
+ title = 'Tour of Heroes';
}
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 926975a..49473b9 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -1,18 +1,48 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
-
+import { FormsModule } from '@angular/forms';
+import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
+import { HeroesComponent } from './heroes/heroes.component';
+import { HeroDetailComponent } from './hero-detail/hero-detail.component';
+import { HeroService } from './hero.service';
+import { MessagesComponent } from './messages/messages.component';
+import { MessageService } from './message.service';
+import { AppRoutingModule } from './/app-routing.module';
+import { DashboardComponent } from './dashboard/dashboard.component';
+import { HeroSearchComponent } from './hero-search/hero-search.component';
+
+import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';
+import { InMemoryDataService } from './in-memory-data.service';
@NgModule({
declarations: [
- AppComponent
+ AppComponent,
+ HeroesComponent,
+ HeroDetailComponent,
+ MessagesComponent,
+ DashboardComponent,
+ HeroSearchComponent
],
imports: [
- BrowserModule
+ BrowserModule,
+ FormsModule,
+ AppRoutingModule,
+ HttpClientModule,
+
+ // The HttpClientInMemoryWebApiModule module intercepts HTTP requests
+ // and returns simulated server responses.
+ // Remove it when a real server is ready to receive requests.
+ HttpClientInMemoryWebApiModule.forRoot(
+ InMemoryDataService, { dataEncapsulation: false }
+ )
+ ],
+ providers: [
+ HeroService,
+ MessageService,
],
- providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
diff --git a/src/app/dashboard/dashboard.component.css b/src/app/dashboard/dashboard.component.css
new file mode 100644
index 0000000..3822cc5
--- /dev/null
+++ b/src/app/dashboard/dashboard.component.css
@@ -0,0 +1,62 @@
+/* DashboardComponent's private CSS styles */
+[class*='col-'] {
+ float: left;
+ padding-right: 20px;
+ padding-bottom: 20px;
+}
+[class*='col-']:last-of-type {
+ padding-right: 0;
+}
+a {
+ text-decoration: none;
+}
+*, *:after, *:before {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+h3 {
+ text-align: center; margin-bottom: 0;
+}
+h4 {
+ position: relative;
+}
+.grid {
+ margin: 0;
+}
+.col-1-4 {
+ width: 25%;
+}
+.module {
+ padding: 20px;
+ text-align: center;
+ color: #eee;
+ max-height: 120px;
+ min-width: 120px;
+ background-color: #607D8B;
+ border-radius: 2px;
+}
+.module:hover {
+ background-color: #EEE;
+ cursor: pointer;
+ color: #607d8b;
+}
+.grid-pad {
+ padding: 10px 0;
+}
+.grid-pad > [class*='col-']:last-of-type {
+ padding-right: 20px;
+}
+@media (max-width: 600px) {
+ .module {
+ font-size: 10px;
+ max-height: 75px; }
+}
+@media (max-width: 1024px) {
+ .grid {
+ margin: 0;
+ }
+ .module {
+ min-width: 60px;
+ }
+}
diff --git a/src/app/dashboard/dashboard.component.html b/src/app/dashboard/dashboard.component.html
new file mode 100644
index 0000000..25ce7d7
--- /dev/null
+++ b/src/app/dashboard/dashboard.component.html
@@ -0,0 +1,11 @@
+Top Heroes
+
+
+
diff --git a/src/app/dashboard/dashboard.component.spec.ts b/src/app/dashboard/dashboard.component.spec.ts
new file mode 100644
index 0000000..9c996c3
--- /dev/null
+++ b/src/app/dashboard/dashboard.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { DashboardComponent } from './dashboard.component';
+
+describe('DashboardComponent', () => {
+ let component: DashboardComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ DashboardComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(DashboardComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/dashboard/dashboard.component.ts b/src/app/dashboard/dashboard.component.ts
new file mode 100644
index 0000000..4958533
--- /dev/null
+++ b/src/app/dashboard/dashboard.component.ts
@@ -0,0 +1,24 @@
+import { Component, OnInit } from '@angular/core';
+import { Hero } from '../hero';
+import { HeroService } from '../hero.service';
+
+@Component({
+ selector: 'app-dashboard',
+ templateUrl: './dashboard.component.html',
+ styleUrls: ['./dashboard.component.css']
+})
+export class DashboardComponent implements OnInit {
+ heroes: Hero[] = [];
+
+ constructor(private heroService: HeroService) { }
+
+ ngOnInit() {
+ this.getHeroes();
+ }
+
+ getHeroes(): void {
+ this.heroService.getHeroes()
+ .subscribe(heroes => this.heroes = heroes.slice(1, 5));
+ }
+
+}
diff --git a/src/app/hero-detail/hero-detail.component.css b/src/app/hero-detail/hero-detail.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/hero-detail/hero-detail.component.html b/src/app/hero-detail/hero-detail.component.html
new file mode 100644
index 0000000..e112eb8
--- /dev/null
+++ b/src/app/hero-detail/hero-detail.component.html
@@ -0,0 +1,12 @@
+
+
{{ hero.name | uppercase }} Details
+
id: {{ hero.id }}
+
+
+
+
+
+
+
diff --git a/src/app/hero-detail/hero-detail.component.spec.ts b/src/app/hero-detail/hero-detail.component.spec.ts
new file mode 100644
index 0000000..5e34497
--- /dev/null
+++ b/src/app/hero-detail/hero-detail.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { HeroDetailComponent } from './hero-detail.component';
+
+describe('HeroDetailComponent', () => {
+ let component: HeroDetailComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ HeroDetailComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(HeroDetailComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/hero-detail/hero-detail.component.ts b/src/app/hero-detail/hero-detail.component.ts
new file mode 100644
index 0000000..0af3db0
--- /dev/null
+++ b/src/app/hero-detail/hero-detail.component.ts
@@ -0,0 +1,41 @@
+import { Component, OnInit, Input } from '@angular/core';
+import { Hero } from '../hero';
+import { ActivatedRoute } from '@angular/router';
+import { Location } from '@angular/common';
+
+import { HeroService } from '../hero.service';
+
+@Component({
+ selector: 'app-hero-detail',
+ templateUrl: './hero-detail.component.html',
+ styleUrls: ['./hero-detail.component.css']
+})
+export class HeroDetailComponent implements OnInit {
+
+ @Input() hero: Hero;
+
+ constructor(
+ private route: ActivatedRoute,
+ private heroService: HeroService,
+ private location: Location
+ ) { }
+
+ ngOnInit() {
+ this.getHero();
+ }
+
+ getHero(): void {
+ const id = +this.route.snapshot.paramMap.get('id');
+ this.heroService.getHero(id)
+ .subscribe(h => this.hero = h);
+ }
+
+ goBack(): void {
+ this.location.back();
+ }
+
+ save(): void {
+ this.heroService.updateHero(this.hero)
+ .subscribe(() => this.goBack());
+ }
+}
diff --git a/src/app/hero-search/hero-search.component.css b/src/app/hero-search/hero-search.component.css
new file mode 100644
index 0000000..0f2c8cd
--- /dev/null
+++ b/src/app/hero-search/hero-search.component.css
@@ -0,0 +1,39 @@
+/* HeroSearch private styles */
+.search-result li {
+ border-bottom: 1px solid gray;
+ border-left: 1px solid gray;
+ border-right: 1px solid gray;
+ width:195px;
+ height: 16px;
+ padding: 5px;
+ background-color: white;
+ cursor: pointer;
+ list-style-type: none;
+}
+
+.search-result li:hover {
+ background-color: #607D8B;
+}
+
+.search-result li a {
+ color: #888;
+ display: block;
+ text-decoration: none;
+}
+
+.search-result li a:hover {
+ color: white;
+}
+.search-result li a:active {
+ color: white;
+}
+#search-box {
+ width: 200px;
+ height: 20px;
+}
+
+
+ul.search-result {
+ margin-top: 0;
+ padding-left: 0;
+}
diff --git a/src/app/hero-search/hero-search.component.html b/src/app/hero-search/hero-search.component.html
new file mode 100644
index 0000000..e6f7c65
--- /dev/null
+++ b/src/app/hero-search/hero-search.component.html
@@ -0,0 +1,13 @@
+
diff --git a/src/app/hero-search/hero-search.component.spec.ts b/src/app/hero-search/hero-search.component.spec.ts
new file mode 100644
index 0000000..901bb7f
--- /dev/null
+++ b/src/app/hero-search/hero-search.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { HeroSearchComponent } from './hero-search.component';
+
+describe('HeroSearchComponent', () => {
+ let component: HeroSearchComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ HeroSearchComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(HeroSearchComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/hero-search/hero-search.component.ts b/src/app/hero-search/hero-search.component.ts
new file mode 100644
index 0000000..7d347b7
--- /dev/null
+++ b/src/app/hero-search/hero-search.component.ts
@@ -0,0 +1,43 @@
+import { Component, OnInit } from '@angular/core';
+
+import { Observable } from 'rxjs/Observable';
+import { Subject } from 'rxjs/Subject';
+import { of } from 'rxjs/observable/of';
+
+import {
+ debounceTime, distinctUntilChanged, switchMap
+ } from 'rxjs/operators';
+
+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 {
+ heroes$: Observable;
+ private searchTerms = new Subject();
+
+ constructor(private heroService: HeroService) {}
+
+ // Push a search term into the observable stream.
+ search(term: string): void {
+ this.searchTerms.next(term);
+ }
+
+ ngOnInit(): void {
+ this.heroes$ = this.searchTerms.pipe(
+ // wait 300ms after each keystroke before considering the term
+ debounceTime(300),
+
+ // ignore new term if same as previous term
+ distinctUntilChanged(),
+
+ // switch to new search observable each time the term changes
+ switchMap((term: string) => this.heroService.searchHeroes(term)),
+ );
+ }
+}
diff --git a/src/app/hero.service.spec.ts b/src/app/hero.service.spec.ts
new file mode 100644
index 0000000..3097bd6
--- /dev/null
+++ b/src/app/hero.service.spec.ts
@@ -0,0 +1,15 @@
+import { TestBed, inject } from '@angular/core/testing';
+
+import { HeroService } from './hero.service';
+
+describe('HeroService', () => {
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ providers: [HeroService]
+ });
+ });
+
+ it('should be created', inject([HeroService], (service: HeroService) => {
+ expect(service).toBeTruthy();
+ }));
+});
diff --git a/src/app/hero.service.ts b/src/app/hero.service.ts
new file mode 100644
index 0000000..384956e
--- /dev/null
+++ b/src/app/hero.service.ts
@@ -0,0 +1,102 @@
+import { Injectable } from '@angular/core';
+import { Hero } from './hero';
+import { HEROES } from './mock-heroes';
+import { Observable } from 'rxjs/Observable';
+import { of } from 'rxjs/observable/of';
+import { MessageService } from './message.service';
+import { HttpClient, HttpHeaders } from '@angular/common/http';
+import { catchError, map, tap } from 'rxjs/operators';
+
+const httpOptions = {
+ headers: new HttpHeaders({ 'Content-Type': 'application/json' })
+};
+
+@Injectable()
+export class HeroService {
+
+ private heroesUrl = 'api/heroes';
+
+ constructor(
+ private http: HttpClient,
+ private messageService: MessageService
+ ) { }
+
+ getHeroes(): Observable {
+ return this.http.get(this.heroesUrl)
+ .pipe(
+ tap(heroes => this.log('fetched heroes')),
+ catchError(this.handleError('getHeroes', []))
+ );
+ }
+
+ getHero(id: number): Observable {
+ const heroUrl = `${this.heroesUrl}/${id}`;
+ return this.http.get(heroUrl)
+ .pipe(
+ tap(_ => this.log(`fetched hero id=${id}`)),
+ catchError(this.handleError(`getHero id=${id}`))
+ );
+ }
+
+ 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'))
+ );
+ }
+
+ addHero(hero: Hero): Observable {
+ return this.http.post(this.heroesUrl, hero, httpOptions)
+ .pipe(
+ tap(_ => this.log(`added hero id=${hero.id}`)),
+ catchError(this.handleError('addHero'))
+ );
+ }
+
+ /** 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'))
+ );
+ }
+
+ searchHeroes(term: string): Observable {
+ term = term.trim();
+ if (!term) { return of([]); }
+ return this.http.get(`api/heroes/?name=${term}`)
+ .pipe(
+ tap(_ => this.log(`found heroes matching "${term}"`)),
+ catchError(this.handleError('searchHeroes', []))
+ );
+ }
+
+ private log(message: string) {
+ this.messageService.add('HeroService: ' + message);
+ }
+
+ /**
+ * 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/src/app/hero.ts b/src/app/hero.ts
new file mode 100644
index 0000000..e3eac51
--- /dev/null
+++ b/src/app/hero.ts
@@ -0,0 +1,4 @@
+export class Hero {
+ id: number;
+ name: string;
+}
diff --git a/src/app/heroes/heroes.component.css b/src/app/heroes/heroes.component.css
new file mode 100644
index 0000000..05b8221
--- /dev/null
+++ b/src/app/heroes/heroes.component.css
@@ -0,0 +1,73 @@
+/* HeroesComponent's private CSS styles */
+.heroes {
+ margin: 0 0 2em 0;
+ list-style-type: none;
+ padding: 0;
+ width: 15em;
+}
+.heroes li {
+ position: relative;
+ cursor: pointer;
+ background-color: #EEE;
+ margin: .5em;
+ padding: .3em 0;
+ height: 1.6em;
+ border-radius: 4px;
+}
+
+.heroes li:hover {
+ color: #607D8B;
+ background-color: #DDD;
+ left: .1em;
+}
+
+.heroes a {
+ color: #888;
+ text-decoration: none;
+ position: relative;
+ display: block;
+ width: 250px;
+}
+
+.heroes a:hover {
+ color:#607D8B;
+}
+
+.heroes .badge {
+ display: inline-block;
+ font-size: small;
+ color: white;
+ padding: 0.8em 0.7em 0 0.7em;
+ background-color: #607D8B;
+ line-height: 1em;
+ position: relative;
+ left: -1px;
+ top: -4px;
+ height: 1.8em;
+ min-width: 16px;
+ text-align: right;
+ 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;
+}
diff --git a/src/app/heroes/heroes.component.html b/src/app/heroes/heroes.component.html
new file mode 100644
index 0000000..f9148a0
--- /dev/null
+++ b/src/app/heroes/heroes.component.html
@@ -0,0 +1,21 @@
+ My Heroes
+
+
+
+
+
+
+
+
diff --git a/src/app/heroes/heroes.component.spec.ts b/src/app/heroes/heroes.component.spec.ts
new file mode 100644
index 0000000..66518e4
--- /dev/null
+++ b/src/app/heroes/heroes.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { HeroesComponent } from './heroes.component';
+
+describe('HeroesComponent', () => {
+ let component: HeroesComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ HeroesComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(HeroesComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/heroes/heroes.component.ts b/src/app/heroes/heroes.component.ts
new file mode 100644
index 0000000..db03e34
--- /dev/null
+++ b/src/app/heroes/heroes.component.ts
@@ -0,0 +1,43 @@
+import { Component, OnInit } from '@angular/core';
+import { Hero } from '../hero';
+import { HeroService } from '../hero.service';
+
+@Component({
+ selector: 'app-heroes',
+ templateUrl: './heroes.component.html',
+ styleUrls: ['./heroes.component.css']
+})
+export class HeroesComponent implements OnInit {
+
+ heroes: Hero[];
+
+ selectedHero: Hero;
+
+ constructor(private heroService: HeroService) { }
+
+ ngOnInit() {
+ this.getHeroes();
+ }
+
+ onSelect(h: Hero): void {
+ this.selectedHero = h;
+ }
+
+ getHeroes():void {
+ 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();
+ }
+
+}
diff --git a/src/app/in-memory-data.service.ts b/src/app/in-memory-data.service.ts
new file mode 100644
index 0000000..ff1f190
--- /dev/null
+++ b/src/app/in-memory-data.service.ts
@@ -0,0 +1,19 @@
+import { InMemoryDbService } from 'angular-in-memory-web-api';
+
+export class InMemoryDataService implements InMemoryDbService {
+ createDb() {
+ const heroes = [
+ { id: 11, name: 'Mr. 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};
+ }
+}
diff --git a/src/app/message.service.spec.ts b/src/app/message.service.spec.ts
new file mode 100644
index 0000000..63ecfd8
--- /dev/null
+++ b/src/app/message.service.spec.ts
@@ -0,0 +1,15 @@
+import { TestBed, inject } from '@angular/core/testing';
+
+import { MessageService } from './message.service';
+
+describe('MessageService', () => {
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ providers: [MessageService]
+ });
+ });
+
+ it('should be created', inject([MessageService], (service: MessageService) => {
+ expect(service).toBeTruthy();
+ }));
+});
diff --git a/src/app/message.service.ts b/src/app/message.service.ts
new file mode 100644
index 0000000..59ac8fe
--- /dev/null
+++ b/src/app/message.service.ts
@@ -0,0 +1,16 @@
+import { Injectable } from '@angular/core';
+
+@Injectable()
+export class MessageService {
+
+ messages: string[] = [];
+
+ add(message: string) {
+ this.messages.push(message);
+ }
+
+ clear() {
+ this.messages = [];
+ }
+
+}
diff --git a/src/app/messages/messages.component.css b/src/app/messages/messages.component.css
new file mode 100644
index 0000000..d08d9be
--- /dev/null
+++ b/src/app/messages/messages.component.css
@@ -0,0 +1,35 @@
+/* MessagesComponent's private CSS styles */
+h2 {
+ color: red;
+ font-family: Arial, Helvetica, sans-serif;
+ font-weight: lighter;
+}
+body {
+ margin: 2em;
+}
+body, input[text], button {
+ color: crimson;
+ font-family: Cambria, Georgia;
+}
+
+button.clear {
+ font-family: Arial;
+ background-color: #eee;
+ border: none;
+ padding: 5px 10px;
+ border-radius: 4px;
+ cursor: pointer;
+ cursor: hand;
+}
+button:hover {
+ background-color: #cfd8dc;
+}
+button:disabled {
+ background-color: #eee;
+ color: #aaa;
+ cursor: auto;
+}
+button.clear {
+ color: #888;
+ margin-bottom: 12px;
+}
diff --git a/src/app/messages/messages.component.html b/src/app/messages/messages.component.html
new file mode 100644
index 0000000..1df7dfd
--- /dev/null
+++ b/src/app/messages/messages.component.html
@@ -0,0 +1,8 @@
+
+
+
Messages
+
+
{{message}}
+
+
diff --git a/src/app/messages/messages.component.spec.ts b/src/app/messages/messages.component.spec.ts
new file mode 100644
index 0000000..66109cc
--- /dev/null
+++ b/src/app/messages/messages.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MessagesComponent } from './messages.component';
+
+describe('MessagesComponent', () => {
+ let component: MessagesComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ MessagesComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(MessagesComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/messages/messages.component.ts b/src/app/messages/messages.component.ts
new file mode 100644
index 0000000..95165a5
--- /dev/null
+++ b/src/app/messages/messages.component.ts
@@ -0,0 +1,16 @@
+import { Component, OnInit } from '@angular/core';
+import { MessageService } from '../message.service';
+
+@Component({
+ selector: 'app-messages',
+ templateUrl: './messages.component.html',
+ styleUrls: ['./messages.component.css']
+})
+export class MessagesComponent implements OnInit {
+
+ constructor(public messageService: MessageService) { }
+
+ ngOnInit() {
+ }
+
+}
diff --git a/src/app/mock-heroes.ts b/src/app/mock-heroes.ts
new file mode 100644
index 0000000..e84c2fd
--- /dev/null
+++ b/src/app/mock-heroes.ts
@@ -0,0 +1,14 @@
+import { Hero } from './hero';
+
+export const HEROES: Hero[] = [
+ { id: 11, name: 'Mr. 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' }
+];
diff --git a/src/styles.css b/src/styles.css
index 90d4ee0..c2be593 100644
--- a/src/styles.css
+++ b/src/styles.css
@@ -1 +1,23 @@
/* You can add global styles to this file, and also import other style files */
+/* Application-wide Styles */
+h1 {
+ color: #369;
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 250%;
+}
+h2, h3 {
+ color: #444;
+ font-family: Arial, Helvetica, sans-serif;
+ font-weight: lighter;
+}
+body {
+ margin: 2em;
+}
+body, input[text], button {
+ color: #888;
+ font-family: Cambria, Georgia;
+}
+/* everywhere else */
+* {
+ font-family: Arial, Helvetica, sans-serif;
+}