| @@ -0,0 +1,19 @@ | |||||
| 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: '', redirectTo: '/dashboard', pathMatch: 'full'}, | |||||
| {path: 'detail/:id', component: HeroDetailComponent} | |||||
| ]; | |||||
| @NgModule({ | |||||
| imports: [RouterModule.forRoot(routes)], | |||||
| exports: [RouterModule] | |||||
| }) | |||||
| export class AppRoutingModule { | |||||
| } | |||||
| @@ -0,0 +1,28 @@ | |||||
| /* AppComponent's private CSS styles */ | |||||
| h1 { | |||||
| font-size: 1.2em; | |||||
| 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: #334953; | |||||
| } | |||||
| nav a:hover { | |||||
| color: #039be5; | |||||
| background-color: #cfd8dc; | |||||
| } | |||||
| nav a.active { | |||||
| color: #039be5; | |||||
| } | |||||
| @@ -1,3 +1,7 @@ | |||||
| <h1>{{title}}</h1> | <h1>{{title}}</h1> | ||||
| <app-heroes></app-heroes> | |||||
| <nav> | |||||
| <a routerLink="/dashboard">Dashboard</a> | |||||
| <a routerLink="/heroes">Heroes</a> | |||||
| </nav> | |||||
| <router-outlet></router-outlet> | |||||
| <app-messages></app-messages> | <app-messages></app-messages> | ||||
| @@ -6,17 +6,21 @@ import { HeroesComponent } from './heroes/heroes.component'; | |||||
| import {FormsModule} from '@angular/forms'; | import {FormsModule} from '@angular/forms'; | ||||
| import { HeroDetailComponent } from './hero-detail/hero-detail.component'; | import { HeroDetailComponent } from './hero-detail/hero-detail.component'; | ||||
| import { MessagesComponent } from './messages/messages.component'; | import { MessagesComponent } from './messages/messages.component'; | ||||
| import { AppRoutingModule } from './app-routing.module'; | |||||
| import { DashboardComponent } from './dashboard/dashboard.component'; | |||||
| @NgModule({ | @NgModule({ | ||||
| declarations: [ | declarations: [ | ||||
| AppComponent, | AppComponent, | ||||
| HeroesComponent, | HeroesComponent, | ||||
| HeroDetailComponent, | HeroDetailComponent, | ||||
| MessagesComponent | |||||
| MessagesComponent, | |||||
| DashboardComponent | |||||
| ], | ], | ||||
| imports: [ | imports: [ | ||||
| BrowserModule, | BrowserModule, | ||||
| FormsModule | |||||
| FormsModule, | |||||
| AppRoutingModule | |||||
| ], | ], | ||||
| providers: [], | providers: [], | ||||
| bootstrap: [AppComponent] | bootstrap: [AppComponent] | ||||
| @@ -0,0 +1,63 @@ | |||||
| /* 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: #3f525c; | |||||
| 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; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,9 @@ | |||||
| <h3>Top Heroes</h3> | |||||
| <div class="grid grid-pad"> | |||||
| <a *ngFor="let hero of heroes" class="col-1-4" | |||||
| routerLink="/detail/{{hero.id}}"> | |||||
| <div class="module hero"> | |||||
| <h4>{{hero.name}}</h4> | |||||
| </div> | |||||
| </a> | |||||
| </div> | |||||
| @@ -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<DashboardComponent>; | |||||
| beforeEach(async(() => { | |||||
| TestBed.configureTestingModule({ | |||||
| declarations: [ DashboardComponent ] | |||||
| }) | |||||
| .compileComponents(); | |||||
| })); | |||||
| beforeEach(() => { | |||||
| fixture = TestBed.createComponent(DashboardComponent); | |||||
| component = fixture.componentInstance; | |||||
| fixture.detectChanges(); | |||||
| }); | |||||
| it('should create', () => { | |||||
| expect(component).toBeTruthy(); | |||||
| }); | |||||
| }); | |||||
| @@ -0,0 +1,23 @@ | |||||
| 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)); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,31 @@ | |||||
| /* HeroDetailComponent's private CSS styles */ | |||||
| label { | |||||
| display: inline-block; | |||||
| width: 3em; | |||||
| margin: .5em 0; | |||||
| color: #607D8B; | |||||
| font-weight: bold; | |||||
| } | |||||
| input { | |||||
| height: 2em; | |||||
| font-size: 1em; | |||||
| padding-left: .4em; | |||||
| } | |||||
| button { | |||||
| margin-top: 20px; | |||||
| 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: #ccc; | |||||
| cursor: auto; | |||||
| } | |||||
| @@ -7,3 +7,4 @@ | |||||
| </label> | </label> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <button (click)="goBack()">go back</button> | |||||
| @@ -1,5 +1,8 @@ | |||||
| import {Component, Input, OnInit} from '@angular/core'; | import {Component, Input, OnInit} from '@angular/core'; | ||||
| import { Hero } from '../hero'; | |||||
| import {Hero} from '../hero'; | |||||
| import {ActivatedRoute} from '@angular/router'; | |||||
| import {Location} from '@angular/common'; | |||||
| import {HeroService} from '../hero.service'; | |||||
| @Component({ | @Component({ | ||||
| selector: 'app-hero-detail', | selector: 'app-hero-detail', | ||||
| @@ -10,9 +13,27 @@ export class HeroDetailComponent implements OnInit { | |||||
| @Input() hero: Hero; | @Input() hero: Hero; | ||||
| constructor() { } | |||||
| constructor( | |||||
| private route: ActivatedRoute, | |||||
| private heroService: HeroService, | |||||
| private location: Location | |||||
| ) { | |||||
| } | |||||
| ngOnInit() { | ngOnInit() { | ||||
| this.getHero(); | |||||
| } | |||||
| private getHero() { | |||||
| // Route parameters are always strings. | |||||
| // The JavaScript (+) operator converts the string to a number, | |||||
| // which is what a hero id should be. | |||||
| const id = +this.route.snapshot.paramMap.get('id'); | |||||
| this.heroService.getHero(id) | |||||
| .subscribe(hero => this.hero = hero); | |||||
| } | } | ||||
| goBack() { | |||||
| this.location.back(); | |||||
| } | |||||
| } | } | ||||
| @@ -16,4 +16,9 @@ export class HeroService { | |||||
| this.messageService.add('HeroService: fetched heroes') | this.messageService.add('HeroService: fetched heroes') | ||||
| return of(HEROES); | return of(HEROES); | ||||
| } | } | ||||
| getHero(id: number): Observable<Hero> { | |||||
| this.messageService.add(`HeroService: fetched hero id=${id}`); | |||||
| return of(HEROES.find(hero => hero.id === id)); | |||||
| } | |||||
| } | } | ||||
| @@ -6,28 +6,33 @@ | |||||
| width: 15em; | width: 15em; | ||||
| } | } | ||||
| .heroes li { | .heroes li { | ||||
| cursor: pointer; | |||||
| position: relative; | position: relative; | ||||
| left: 0; | |||||
| cursor: pointer; | |||||
| background-color: #EEE; | background-color: #EEE; | ||||
| margin: .5em; | margin: .5em; | ||||
| padding: .3em 0; | padding: .3em 0; | ||||
| height: 1.6em; | height: 1.6em; | ||||
| border-radius: 4px; | border-radius: 4px; | ||||
| } | } | ||||
| .heroes li:hover { | .heroes li:hover { | ||||
| color: #607D8B; | color: #607D8B; | ||||
| background-color: #DDD; | background-color: #DDD; | ||||
| left: .1em; | left: .1em; | ||||
| } | } | ||||
| .heroes li.selected { | |||||
| background-color: #CFD8DC; | |||||
| color: white; | |||||
| .heroes a { | |||||
| color: #333; | |||||
| text-decoration: none; | |||||
| position: relative; | |||||
| display: block; | |||||
| width: 250px; | |||||
| } | } | ||||
| .heroes li.selected:hover { | |||||
| background-color: #BBD8DC; | |||||
| color: white; | |||||
| .heroes a:hover { | |||||
| color:#607D8B; | |||||
| } | } | ||||
| .heroes .badge { | .heroes .badge { | ||||
| display: inline-block; | display: inline-block; | ||||
| font-size: small; | font-size: small; | ||||
| @@ -39,6 +44,8 @@ | |||||
| left: -1px; | left: -1px; | ||||
| top: -4px; | top: -4px; | ||||
| height: 1.8em; | height: 1.8em; | ||||
| min-width: 16px; | |||||
| text-align: right; | |||||
| margin-right: .8em; | margin-right: .8em; | ||||
| border-radius: 4px 0 0 4px; | border-radius: 4px 0 0 4px; | ||||
| } | } | ||||
| @@ -1,10 +1,8 @@ | |||||
| <h2>My Heroes</h2> | <h2>My Heroes</h2> | ||||
| <ul class="heroes"> | <ul class="heroes"> | ||||
| <li *ngFor="let hero of heroes" | |||||
| [class.selected]="hero === selectedHero" | |||||
| (click)="onSelect(hero)"> | |||||
| <span class="badge">{{hero.id}}</span> {{hero.name}} | |||||
| <li *ngFor="let hero of heroes"> | |||||
| <a routerLink="/detail/{{hero.id}}"> | |||||
| <span class="badge">{{hero.id}}</span> {{hero.name}} | |||||
| </a> | |||||
| </li> | </li> | ||||
| </ul> | </ul> | ||||
| <app-hero-detail [hero]="selectedHero"></app-hero-detail> | |||||
| @@ -1,7 +1,6 @@ | |||||
| import { Component, OnInit } from '@angular/core'; | |||||
| import { Hero } from '../hero'; | |||||
| import { HEROES } from '../mock-heroes'; | |||||
| import { HeroService } from '../hero.service'; | |||||
| import {Component, OnInit} from '@angular/core'; | |||||
| import {Hero} from '../hero'; | |||||
| import {HeroService} from '../hero.service'; | |||||
| @Component({ | @Component({ | ||||
| selector: 'app-heroes', | selector: 'app-heroes', | ||||
| @@ -15,8 +14,6 @@ export class HeroesComponent implements OnInit { | |||||
| heroes: Hero[]; | heroes: Hero[]; | ||||
| selectedHero: Hero; | |||||
| ngOnInit() { | ngOnInit() { | ||||
| this.getHeroes(); | this.getHeroes(); | ||||
| } | } | ||||
| @@ -25,8 +22,4 @@ export class HeroesComponent implements OnInit { | |||||
| this.heroService.getHeroes() | this.heroService.getHeroes() | ||||
| .subscribe(heroes => this.heroes = heroes); | .subscribe(heroes => this.heroes = heroes); | ||||
| } | } | ||||
| onSelect(hero: Hero) { | |||||
| this.selectedHero = hero; | |||||
| } | |||||
| } | } | ||||