diff --git a/angular.json b/angular.json
index d50f4f2..f25b056 100644
--- a/angular.json
+++ b/angular.json
@@ -1,5 +1,8 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
+ "cli": {
+ "analytics": "405a5022-b417-4976-87d8-56f684a7a13b"
+ },
"version": 1,
"newProjectRoot": "projects",
"projects": {
diff --git a/package-lock.json b/package-lock.json
index 0022201..699ff89 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,6 +15,7 @@
"@angular/platform-browser": "~11.2.9",
"@angular/platform-browser-dynamic": "~11.2.9",
"@angular/router": "~11.2.9",
+ "angular-in-memory-web-api": "^0.11.0",
"rxjs": "~6.6.0",
"tslib": "^2.0.0",
"zone.js": "~0.11.3"
@@ -2553,6 +2554,16 @@
"integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=",
"dev": true
},
+ "node_modules/angular-in-memory-web-api": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/angular-in-memory-web-api/-/angular-in-memory-web-api-0.11.0.tgz",
+ "integrity": "sha512-QV1qYHm+Zd+wrvlcPLnAcqqGpOmCN1EUj4rRuYHpek8+QqFFdxBNuPZOJCKvU7I97z5QSKHsdc6PNKlpUQr3UA==",
+ "peerDependencies": {
+ "@angular/common": ">=8.0.0",
+ "@angular/core": ">=8.0.0",
+ "rxjs": "^6.0.0"
+ }
+ },
"node_modules/ansi-colors": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
@@ -20965,6 +20976,12 @@
"integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=",
"dev": true
},
+ "angular-in-memory-web-api": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/angular-in-memory-web-api/-/angular-in-memory-web-api-0.11.0.tgz",
+ "integrity": "sha512-QV1qYHm+Zd+wrvlcPLnAcqqGpOmCN1EUj4rRuYHpek8+QqFFdxBNuPZOJCKvU7I97z5QSKHsdc6PNKlpUQr3UA==",
+ "requires": {}
+ },
"ansi-colors": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
diff --git a/package.json b/package.json
index 0b2c35f..5baff2e 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,7 @@
"@angular/platform-browser": "~11.2.9",
"@angular/platform-browser-dynamic": "~11.2.9",
"@angular/router": "~11.2.9",
+ "angular-in-memory-web-api": "^0.11.0",
"rxjs": "~6.6.0",
"tslib": "^2.0.0",
"zone.js": "~0.11.3"
diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
new file mode 100644
index 0000000..7398bcb
--- /dev/null
+++ b/src/app/app-routing.module.ts
@@ -0,0 +1,21 @@
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+import { HeroesComponent } from './components/heroes/heroes.component';
+import { DashboardComponent } from './components/dashboard/dashboard.component';
+import { HeroDetailComponent } from './components/hero-detail/hero-detail.component';
+import { CommonModule } from '@angular/common';
+
+const routes: Routes = [
+ { path: '', redirectTo: '/dashboard', pathMatch: 'full' },
+ { path: 'dashboard', component: DashboardComponent },
+ { path: 'detail/:id', component: HeroDetailComponent },
+ { path: 'heroes', component: HeroesComponent }
+];
+
+
+@NgModule({
+ imports: [RouterModule.forRoot(routes)],
+ declarations: [],
+ exports: [RouterModule]
+})
+export class AppRoutingModule { }
diff --git a/src/app/app.component.html b/src/app/app.component.html
index 1b04811..c026c65 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -1,519 +1,7 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
{{ title }} app is running!
-
-
-
-
-
-
-
Resources
-
Here are some links to help you get started:
-
-
-
-
-
Next Steps
-
What do you want to do next with your app?
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Run and Watch Tests
-
-
-
-
-
-
Build for Production
-
-
-
-
-
-
ng generate component xyz
-
ng add @angular/material
-
ng add @angular/pwa
-
ng add _____
-
ng test
-
ng build --prod
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+{{title}}
+
+
+
\ No newline at end of file
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 16f9b76..c48691c 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.scss']
})
export class AppComponent {
- title = 'my-app';
+ title = 'Tour of Heroes';
}
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 8dfc1d6..27a2945 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -1,14 +1,34 @@
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
-
+import { FormsModule } from '@angular/forms';
+import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
+import { ComponentOverviewComponent } from './components/component-overview/component-overview.component';
+import { HeroesComponent } from './components/heroes/heroes.component';
+import { MessagesComponent } from './components/messages/messages.component';
+import { HeroDetailComponent } from './components/hero-detail/hero-detail.component';
+import { AppRoutingModule } from './app-routing.module';
+import { DashboardComponent } from './components/dashboard/dashboard.component';
+import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';
+import { InMemoryDataService } from './in-memory-data.service';
@NgModule({
declarations: [
- AppComponent
+ AppComponent,
+ ComponentOverviewComponent,
+ HeroesComponent,
+ MessagesComponent,
+ HeroDetailComponent,
+ DashboardComponent
],
imports: [
- BrowserModule
+ BrowserModule,
+ FormsModule,
+ HttpClientModule,
+ AppRoutingModule,
+ HttpClientInMemoryWebApiModule.forRoot(
+ InMemoryDataService, { dataEncapsulation: false }
+ )
],
providers: [],
bootstrap: [AppComponent]
diff --git a/src/app/components/component-overview/component-overview.component.html b/src/app/components/component-overview/component-overview.component.html
new file mode 100644
index 0000000..ffe0904
--- /dev/null
+++ b/src/app/components/component-overview/component-overview.component.html
@@ -0,0 +1 @@
+component-overview works!
diff --git a/src/app/components/component-overview/component-overview.component.scss b/src/app/components/component-overview/component-overview.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/components/component-overview/component-overview.component.spec.ts b/src/app/components/component-overview/component-overview.component.spec.ts
new file mode 100644
index 0000000..db5ae88
--- /dev/null
+++ b/src/app/components/component-overview/component-overview.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ComponentOverviewComponent } from './component-overview.component';
+
+describe('ComponentOverviewComponent', () => {
+ let component: ComponentOverviewComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ ComponentOverviewComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ComponentOverviewComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/components/component-overview/component-overview.component.ts b/src/app/components/component-overview/component-overview.component.ts
new file mode 100644
index 0000000..a6e9ffc
--- /dev/null
+++ b/src/app/components/component-overview/component-overview.component.ts
@@ -0,0 +1,40 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'app-component-overview',
+ templateUrl: './component-overview.component.html',
+ styleUrls: ['./component-overview.component.scss']
+})
+export class ComponentOverviewComponent implements OnInit {
+
+ constructor() { }
+
+ ngOnInit(): void {
+ console.log("component-overview excute ngOnInit")
+ this.logIt(`OnInit`);
+ }
+ ngOnChanges(): void {
+ console.log("component-overview excute ngOnChanges")
+ }
+ ngDoCheck(): void {
+ console.log("component-overview excute ngDoCheck")
+ }
+ ngAfterContentInit(): void {
+ console.log("component-overview excute ngAfterContentInit")
+ }
+ ngAfterContentChecked(): void {
+ console.log("component-overview excute ngAfterContentChecked")
+ }
+ ngAfterViewInit(): void {
+ console.log("component-overview excute ngAfterViewInit")
+ }
+ ngAfterViewChecked(): void {
+ console.log("component-overview excute ngAfterViewChecked")
+ }
+ ngOnDestroy(): void {
+ console.log("component-overview excute ngOnDestroy")
+ }
+ logIt(msg: string) {
+
+ }
+}
diff --git a/src/app/components/dashboard/dashboard.component.html b/src/app/components/dashboard/dashboard.component.html
new file mode 100644
index 0000000..cd64de5
--- /dev/null
+++ b/src/app/components/dashboard/dashboard.component.html
@@ -0,0 +1,4 @@
+
+ {{hero.name}}
+
\ No newline at end of file
diff --git a/src/app/components/dashboard/dashboard.component.scss b/src/app/components/dashboard/dashboard.component.scss
new file mode 100644
index 0000000..e9d534d
--- /dev/null
+++ b/src/app/components/dashboard/dashboard.component.scss
@@ -0,0 +1,50 @@
+/* DashboardComponent's private CSS styles */
+
+h2 {
+ text-align: center;
+ }
+
+ .heroes-menu {
+ padding: 0;
+ margin: auto;
+ max-width: 1000px;
+
+ /* flexbox */
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ justify-content: space-around;
+ align-content: flex-start;
+ align-items: flex-start;
+ }
+
+ a {
+ background-color: #3f525c;
+ border-radius: 2px;
+ padding: 1rem;
+ font-size: 1.2rem;
+ text-decoration: none;
+ display: inline-block;
+ color: #fff;
+ text-align: center;
+ width: 100%;
+ min-width: 70px;
+ margin: .5rem auto;
+ box-sizing: border-box;
+
+ /* flexbox */
+ order: 0;
+ flex: 0 1 auto;
+ align-self: auto;
+ }
+
+ @media (min-width: 600px) {
+ a {
+ width: 18%;
+ box-sizing: content-box;
+ }
+ }
+
+ a:hover {
+ background-color: #000;
+ }
\ No newline at end of file
diff --git a/src/app/components/dashboard/dashboard.component.spec.ts b/src/app/components/dashboard/dashboard.component.spec.ts
new file mode 100644
index 0000000..5ec4ff8
--- /dev/null
+++ b/src/app/components/dashboard/dashboard.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { DashboardComponent } from './dashboard.component';
+
+describe('DashboardComponent', () => {
+ let component: DashboardComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await 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/components/dashboard/dashboard.component.ts b/src/app/components/dashboard/dashboard.component.ts
new file mode 100644
index 0000000..08a0b12
--- /dev/null
+++ b/src/app/components/dashboard/dashboard.component.ts
@@ -0,0 +1,23 @@
+import { Component, OnInit } from '@angular/core';
+import { Hero } from '../../interface/hero.interface';
+import { HeroService } from '../../services/hero.service';
+
+@Component({
+ selector: 'app-dashboard',
+ templateUrl: './dashboard.component.html',
+ styleUrls: [ './dashboard.component.scss' ]
+})
+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));
+ }
+}
\ No newline at end of file
diff --git a/src/app/components/hero-detail/hero-detail.component.html b/src/app/components/hero-detail/hero-detail.component.html
new file mode 100644
index 0000000..2842d8d
--- /dev/null
+++ b/src/app/components/hero-detail/hero-detail.component.html
@@ -0,0 +1,9 @@
+
+
{{hero.name | uppercase}} Details
+
id: {{hero.id}}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/app/components/hero-detail/hero-detail.component.scss b/src/app/components/hero-detail/hero-detail.component.scss
new file mode 100644
index 0000000..7509fb2
--- /dev/null
+++ b/src/app/components/hero-detail/hero-detail.component.scss
@@ -0,0 +1,24 @@
+/* HeroDetailComponent's private CSS styles */
+label {
+ color: #435960;
+ font-weight: bold;
+ }
+ input {
+ font-size: 1em;
+ padding: .5rem;
+ }
+ button {
+ margin-top: 20px;
+ background-color: #eee;
+ padding: 1rem;
+ border-radius: 4px;
+ font-size: 1rem;
+ }
+ button:hover {
+ background-color: #cfd8dc;
+ }
+ button:disabled {
+ background-color: #eee;
+ color: #ccc;
+ cursor: auto;
+ }
\ No newline at end of file
diff --git a/src/app/components/hero-detail/hero-detail.component.spec.ts b/src/app/components/hero-detail/hero-detail.component.spec.ts
new file mode 100644
index 0000000..6e4873c
--- /dev/null
+++ b/src/app/components/hero-detail/hero-detail.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { HeroDetailComponent } from './hero-detail.component';
+
+describe('HeroDetailComponent', () => {
+ let component: HeroDetailComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await 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/components/hero-detail/hero-detail.component.ts b/src/app/components/hero-detail/hero-detail.component.ts
new file mode 100644
index 0000000..fb55ca6
--- /dev/null
+++ b/src/app/components/hero-detail/hero-detail.component.ts
@@ -0,0 +1,35 @@
+import { Component, OnInit, Input } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import { Location } from '@angular/common';
+
+import { Hero } from '../../interface/hero.interface';
+import { HeroService } from '../../services/hero.service';
+
+@Component({
+ selector: 'app-hero-detail',
+ templateUrl: './hero-detail.component.html',
+ styleUrls: [ './hero-detail.component.scss' ]
+})
+export class HeroDetailComponent implements OnInit {
+ @Input() hero?: Hero;
+
+ constructor(
+ private route: ActivatedRoute,
+ private heroService: HeroService,
+ private location: Location
+ ) {}
+
+ ngOnInit(): void {
+ this.getHero();
+ }
+
+ getHero(): void {
+ const id = Number(this.route.snapshot.paramMap.get('id'));
+ this.heroService.getHero(id)
+ .subscribe(hero => this.hero = hero);
+ }
+
+ goBack(): void {
+ this.location.back();
+ }
+}
\ No newline at end of file
diff --git a/src/app/components/heroes/heroes.component.html b/src/app/components/heroes/heroes.component.html
new file mode 100644
index 0000000..8708ab5
--- /dev/null
+++ b/src/app/components/heroes/heroes.component.html
@@ -0,0 +1,8 @@
+
+
\ No newline at end of file
diff --git a/src/app/components/heroes/heroes.component.scss b/src/app/components/heroes/heroes.component.scss
new file mode 100644
index 0000000..6d52b44
--- /dev/null
+++ b/src/app/components/heroes/heroes.component.scss
@@ -0,0 +1,52 @@
+/* HeroesComponent's private CSS styles */
+.heroes {
+ margin: 0 0 2em 0;
+ list-style-type: none;
+ padding: 0;
+ width: 15em;
+ }
+ .heroes li {
+ cursor: pointer;
+ position: relative;
+ left: 0;
+ background-color: #EEE;
+ margin: .5em;
+ padding: .3em 0;
+ height: 1.6em;
+ border-radius: 4px;
+ }
+ .heroes li:hover {
+ color: #2c3a41;
+ background-color: #e6e6e6;
+ left: .1em;
+ }
+ .heroes li.selected {
+ background-color: black;
+ color: white;
+ }
+ .heroes li.selected:hover {
+ background-color: #505050;
+ color: white;
+ }
+ .heroes li.selected:active {
+ background-color: black;
+ color: white;
+ }
+ .heroes .badge {
+ display: inline-block;
+ font-size: small;
+ color: white;
+ padding: 0.8em 0.7em 0 0.7em;
+ background-color:#405061;
+ line-height: 1em;
+ position: relative;
+ left: -1px;
+ top: -4px;
+ height: 1.8em;
+ margin-right: .8em;
+ border-radius: 4px 0 0 4px;
+ }
+
+ input {
+ padding: .5rem;
+ }
\ No newline at end of file
diff --git a/src/app/components/heroes/heroes.component.spec.ts b/src/app/components/heroes/heroes.component.spec.ts
new file mode 100644
index 0000000..cc2d180
--- /dev/null
+++ b/src/app/components/heroes/heroes.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { HeroesComponent } from './heroes.component';
+
+describe('HeroesComponent', () => {
+ let component: HeroesComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await 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/components/heroes/heroes.component.ts b/src/app/components/heroes/heroes.component.ts
new file mode 100644
index 0000000..3df34a2
--- /dev/null
+++ b/src/app/components/heroes/heroes.component.ts
@@ -0,0 +1,31 @@
+import { Component, OnInit } from '@angular/core';
+import { Hero } from '../../interface/hero.interface';
+import { HeroService } from '../../services/hero.service';
+import { MessageService } from '../../services/message.service';
+
+@Component({
+ selector: 'app-heroes',
+ templateUrl: './heroes.component.html',
+ styleUrls: ['./heroes.component.scss']
+})
+export class HeroesComponent implements OnInit {
+
+ selectedHero?: Hero;
+
+ heroes: Hero[] = [];
+
+ constructor(private heroService: HeroService, private messageService: MessageService) { }
+
+ ngOnInit() {
+ this.getHeroes();
+ }
+
+ onSelect(hero: Hero): void {
+ console.log("heroes.component.ts excite onSelect")
+ this.selectedHero = hero;
+ this.messageService.add(`HeroesComponent: Selected hero id=${hero.id}`);
+ }
+ getHeroes(): void {
+ this.heroService.getHeroes()
+ .subscribe(heroes => this.heroes = heroes); }
+}
\ No newline at end of file
diff --git a/src/app/components/messages/messages.component.html b/src/app/components/messages/messages.component.html
new file mode 100644
index 0000000..dd1b94d
--- /dev/null
+++ b/src/app/components/messages/messages.component.html
@@ -0,0 +1,8 @@
+
+
+
Messages
+
+
{{message}}
+
+
\ No newline at end of file
diff --git a/src/app/components/messages/messages.component.scss b/src/app/components/messages/messages.component.scss
new file mode 100644
index 0000000..65eb04b
--- /dev/null
+++ b/src/app/components/messages/messages.component.scss
@@ -0,0 +1,19 @@
+/* MessagesComponent's private CSS styles */
+h2 {
+ color: #A80000;
+ font-family: Arial, Helvetica, sans-serif;
+ font-weight: lighter;
+ }
+
+ .clear {
+ color: #333;
+ background-color: #eee;
+ margin-bottom: 12px;
+ padding: 1rem;
+ border-radius: 4px;
+ font-size: 1rem;
+ }
+ .clear:hover {
+ color: white;
+ background-color: #42545C;
+ }
\ No newline at end of file
diff --git a/src/app/components/messages/messages.component.spec.ts b/src/app/components/messages/messages.component.spec.ts
new file mode 100644
index 0000000..69163f1
--- /dev/null
+++ b/src/app/components/messages/messages.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MessagesComponent } from './messages.component';
+
+describe('MessagesComponent', () => {
+ let component: MessagesComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await 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/components/messages/messages.component.ts b/src/app/components/messages/messages.component.ts
new file mode 100644
index 0000000..8a3d0d0
--- /dev/null
+++ b/src/app/components/messages/messages.component.ts
@@ -0,0 +1,16 @@
+import { Component, OnInit } from '@angular/core';
+import { MessageService } from '../../services/message.service';
+
+@Component({
+ selector: 'app-messages',
+ templateUrl: './messages.component.html',
+ styleUrls: ['./messages.component.scss']
+})
+export class MessagesComponent implements OnInit {
+
+ constructor(public messageService: MessageService) {}
+
+ ngOnInit() {
+ }
+
+}
\ No newline at end of file
diff --git a/src/app/interface/hero.interface.ts b/src/app/interface/hero.interface.ts
new file mode 100644
index 0000000..364f330
--- /dev/null
+++ b/src/app/interface/hero.interface.ts
@@ -0,0 +1,4 @@
+export interface Hero {
+ id: number;
+ name: string;
+}
\ No newline at end of file
diff --git a/src/app/interface/mock-heroes.ts b/src/app/interface/mock-heroes.ts
new file mode 100644
index 0000000..a1aa80d
--- /dev/null
+++ b/src/app/interface/mock-heroes.ts
@@ -0,0 +1,14 @@
+import { Hero } from './hero.interface';
+
+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' }
+];
\ No newline at end of file
diff --git a/src/app/services/hero.service.spec.ts b/src/app/services/hero.service.spec.ts
new file mode 100644
index 0000000..42311f5
--- /dev/null
+++ b/src/app/services/hero.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { HeroService } from './hero.service';
+
+describe('HeroService', () => {
+ let service: HeroService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(HeroService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/src/app/services/hero.service.ts b/src/app/services/hero.service.ts
new file mode 100644
index 0000000..842a1be
--- /dev/null
+++ b/src/app/services/hero.service.ts
@@ -0,0 +1,27 @@
+import { Injectable } from '@angular/core';
+import { Observable, of } from 'rxjs';
+
+import { Hero } from '../interface/hero.interface';
+import { HEROES } from '../interface/mock-heroes';
+import { MessageService } from './message.service';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class HeroService {
+
+ constructor(private messageService: MessageService) { }
+ getHeroes(): Observable {
+ const heroes = of(HEROES);
+ this.messageService.add('HeroService: fetched heroes');
+ return heroes;
+ }
+
+ getHero(id: number): Observable {
+ // For now, assume that a hero with the specified `id` always exists.
+ // Error handling will be added in the next step of the tutorial.
+ const hero = HEROES.find(h => h.id === id) as Hero;
+ this.messageService.add(`HeroService: fetched hero id=${id}`);
+ return of(hero);
+ }
+}
\ No newline at end of file
diff --git a/src/app/services/message.service.spec.ts b/src/app/services/message.service.spec.ts
new file mode 100644
index 0000000..1db761b
--- /dev/null
+++ b/src/app/services/message.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { MessageService } from './message.service';
+
+describe('MessageService', () => {
+ let service: MessageService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(MessageService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/src/app/services/message.service.ts b/src/app/services/message.service.ts
new file mode 100644
index 0000000..045ebbe
--- /dev/null
+++ b/src/app/services/message.service.ts
@@ -0,0 +1,16 @@
+import { Injectable } from '@angular/core';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class MessageService {
+ messages: string[] = [];
+
+ add(message: string) {
+ this.messages.push(message);
+ }
+
+ clear() {
+ this.messages = [];
+ }
+}
\ No newline at end of file