diff --git a/app/app-routing.module.ts b/app/app-routing.module.ts
new file mode 100644
index 0000000..73f447f
--- /dev/null
+++ b/app/app-routing.module.ts
@@ -0,0 +1,20 @@
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+
+import { DashboardComponent } from './dashboard.component';
+import { HeroesComponent } from './heroes.component';
+import { HeroDetailComponent } from './hero-detail.component';
+
+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) ],
+ exports: [ RouterModule ]
+})
+
+export class AppRoutingModule {}
diff --git a/app/app.component.css b/app/app.component.css
new file mode 100644
index 0000000..40e1aba
--- /dev/null
+++ b/app/app.component.css
@@ -0,0 +1,28 @@
+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/app/app.component.ts b/app/app.component.ts
index 4c06d2f..cb4c452 100644
--- a/app/app.component.ts
+++ b/app/app.component.ts
@@ -1,6 +1,19 @@
import { Component } from '@angular/core';
+
@Component({
+ moduleId: module.id,
selector: 'my-app',
- template: '
My First Angular App
'
+ template:`
+ {{title}}
+
+
+ `,
+ styleUrls: [ 'app.component.css' ]
})
-export class AppComponent { }
+
+export class AppComponent {
+ title = 'Tour of Heroes';
+}
diff --git a/app/app.module.ts b/app/app.module.ts
index 5117901..f9e9c8a 100644
--- a/app/app.module.ts
+++ b/app/app.module.ts
@@ -1,12 +1,41 @@
import { NgModule } from '@angular/core';
-// ブラウザで表示させるために必要
import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms';
+import { HttpModule } from '@angular/http';
+
+import { AppComponent } from './app.component';
+import { DashboardComponent } from './dashboard.component';
+import { HeroesComponent } from './heroes.component';
+import { HeroDetailComponent } from './hero-detail.component';
+import { HeroSearchComponent } from './hero-search.component'
+
+import { HeroService } from './hero.service';
+
+import { AppRoutingModule } from './app-routing.module';
+
+import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
+import { InMemoryDataService } from './in-memory-data.service';
+
-// AppComponent を読みこんでる
-import { AppComponent } from './app.component';
@NgModule({
- imports: [ BrowserModule ],
- declarations: [ AppComponent ],
- bootstrap: [ AppComponent ]
+ imports: [
+ BrowserModule,
+ FormsModule,
+ HttpModule,
+ InMemoryWebApiModule.forRoot(InMemoryDataService),
+ AppRoutingModule
+ ],
+ declarations: [
+ AppComponent,
+ DashboardComponent,
+ HeroesComponent,
+ HeroSearchComponent,
+ HeroDetailComponent
+ ],
+ providers: [
+ HeroService
+ ],
+ bootstrap: [ AppComponent ]
})
+
export class AppModule { }
diff --git a/app/dashboard.component.css b/app/dashboard.component.css
new file mode 100644
index 0000000..096cec7
--- /dev/null
+++ b/app/dashboard.component.css
@@ -0,0 +1,61 @@
+[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/app/dashboard.component.html b/app/dashboard.component.html
new file mode 100644
index 0000000..937931a
--- /dev/null
+++ b/app/dashboard.component.html
@@ -0,0 +1,9 @@
+Top Heroes
+
+
diff --git a/app/dashboard.component.ts b/app/dashboard.component.ts
new file mode 100644
index 0000000..cb45b10
--- /dev/null
+++ b/app/dashboard.component.ts
@@ -0,0 +1,22 @@
+import { Component, OnInit } from '@angular/core';
+
+import { Hero } from './hero';
+import { HeroService } from './hero.service';
+
+@Component({
+ moduleId: module.id,
+ selector: 'my-dashboard',
+ templateUrl: 'dashboard.component.html',
+ styleUrls: [ 'dashboard.component.css' ]
+})
+
+export class DashboardComponent {
+ heroes: Hero[] = [];
+
+ constructor(private heroService: HeroService) {}
+
+ ngOnInit(): void {
+ this.heroService.getHeroes()
+ .then(heroes => this.heroes = heroes.slice(1, 5));
+ }
+}
diff --git a/app/hero-detail.component.css b/app/hero-detail.component.css
new file mode 100644
index 0000000..f6139ba
--- /dev/null
+++ b/app/hero-detail.component.css
@@ -0,0 +1,29 @@
+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;
+}
diff --git a/app/hero-detail.component.html b/app/hero-detail.component.html
new file mode 100644
index 0000000..9ef390c
--- /dev/null
+++ b/app/hero-detail.component.html
@@ -0,0 +1,11 @@
+
+
{{hero.name}} details!
+
+ {{hero.id}}
+
+
+
+
+
+
+
diff --git a/app/hero-detail.component.ts b/app/hero-detail.component.ts
new file mode 100644
index 0000000..a8ed5bb
--- /dev/null
+++ b/app/hero-detail.component.ts
@@ -0,0 +1,40 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, Params } from '@angular/router';
+import { Location } from '@angular/common';
+
+import 'rxjs/add/operator/switchMap';
+
+import { Hero } from './hero';
+import { HeroService } from './hero.service';
+
+@Component({
+ moduleId: module.id,
+ selector: 'my-hero-detail',
+ templateUrl: 'hero-detail.component.html',
+ styleUrls: [ 'hero-detail.component.css' ]
+})
+
+export class HeroDetailComponent implements OnInit {
+ hero: Hero;
+
+ ngOnInit(): void {
+ this.route.params
+ .switchMap((params: Params) => this.heroService.getHero(+params['id']))
+ .subscribe(hero => this.hero = hero);
+ }
+
+ constructor(
+ private heroService: HeroService,
+ private route: ActivatedRoute,
+ private location: Location
+ ) {}
+
+ save(): void {
+ this.heroService.update(this.hero)
+ .then(() => this.goBack());
+ }
+
+ goBack(): void {
+ this.location.back();
+ }
+}
diff --git a/app/hero-search.component.css b/app/hero-search.component.css
new file mode 100644
index 0000000..f6b5c59
--- /dev/null
+++ b/app/hero-search.component.css
@@ -0,0 +1,14 @@
+.search-result{
+ border-bottom: 1px solid gray;
+ border-left: 1px solid gray;
+ border-right: 1px solid gray;
+ width:195px;
+ height: 20px;
+ padding: 5px;
+ background-color: white;
+ cursor: pointer;
+}
+#search-box{
+ width: 200px;
+ height: 20px;
+}
diff --git a/app/hero-search.component.html b/app/hero-search.component.html
new file mode 100644
index 0000000..15d7c6c
--- /dev/null
+++ b/app/hero-search.component.html
@@ -0,0 +1,8 @@
+
+
Hero Search
+
+
diff --git a/app/hero-search.component.ts b/app/hero-search.component.ts
new file mode 100644
index 0000000..fc827be
--- /dev/null
+++ b/app/hero-search.component.ts
@@ -0,0 +1,58 @@
+import { Component, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+
+import { Observable } from 'rxjs/Observable';
+import { Subject } from 'rxjs/Subject';
+
+// Observable class extensions
+import 'rxjs/add/observable/of';
+
+// Observable operators
+import 'rxjs/add/operator/catch';
+import 'rxjs/add/operator/debounceTime';
+import 'rxjs/add/operator/distinctUntilChanged';
+
+import { HeroSearchService } from './hero-search.service';
+import { Hero } from './hero';
+
+@Component({
+ moduleId: module.id,
+ selector: 'hero-search',
+ templateUrl: './hero-search.component.html',
+ styleUrls: [ './hero-search.component.css' ],
+ providers: [HeroSearchService]
+})
+export class HeroSearchComponent implements OnInit {
+ heroes: Observable
;
+ private searchTerms = new Subject();
+
+ constructor(
+ private heroSearchService: HeroSearchService,
+ private router: Router) {}
+
+ // Push a search term into the observable stream.
+ search(term: string): void {
+ this.searchTerms.next(term);
+ }
+
+ ngOnInit(): void {
+ this.heroes = this.searchTerms
+ .debounceTime(300) // wait 300ms after each keystroke before considering the term
+ .distinctUntilChanged() // ignore if next search term is same as previous
+ .switchMap(term => term // switch to new observable each time the term changes
+ // return the http search observable
+ ? this.heroSearchService.search(term)
+ // or the observable of empty heroes if there was no search term
+ : Observable.of([]))
+ .catch(error => {
+ // TODO: add real error handling
+ console.log(error);
+ return Observable.of([]);
+ });
+ }
+
+ gotoDetail(hero: Hero): void {
+ let link = ['/detail', hero.id];
+ this.router.navigate(link);
+ }
+}
diff --git a/app/hero-search.service.ts b/app/hero-search.service.ts
new file mode 100644
index 0000000..23fbffa
--- /dev/null
+++ b/app/hero-search.service.ts
@@ -0,0 +1,17 @@
+import { Injectable } from '@angular/core';
+import { Http } from '@angular/http';
+
+import { Observable } from 'rxjs/Observable';
+import 'rxjs/add/operator/map';
+
+import { Hero } from './hero';
+
+@Injectable()
+export class HeroSearchService {
+ constructor(private http: Http) {}
+
+ search(term: string): Observable {
+ return this.http.get(`app/heroes/?name=${term}`)
+ .map(response => response.json().data as Hero[]);
+ }
+}
diff --git a/app/hero.service.ts b/app/hero.service.ts
new file mode 100644
index 0000000..8de8c76
--- /dev/null
+++ b/app/hero.service.ts
@@ -0,0 +1,53 @@
+import { Injectable } from '@angular/core';
+import { Headers, Http } from '@angular/http';
+
+import 'rxjs/add/operator/toPromise';
+
+import { Hero } from './hero';
+
+@Injectable()
+export class HeroService {
+ private heroesUrl = 'api/heroes'; // URL to web api
+ private headers = new Headers({'Content-Type': 'application/json'});
+
+ constructor(private http: Http) { }
+
+ getHeroes(): Promise {
+ return this.http.get(this.heroesUrl)
+ .toPromise()
+ .then(response => response.json().data as Hero[])
+ .catch(this.handleError);
+ }
+ getHero(id: number): Promise {
+ const url = `${this.heroesUrl}/${id}`;
+ return this.http.get(url)
+ .toPromise()
+ .then(response => response.json().data as Hero)
+ .catch(this.handleError);
+ }
+ create(name: string): Promise {
+ return this.http.post(this.heroesUrl, JSON.stringify({name: name}), {headers: this.headers})
+ .toPromise()
+ .then(res => res.json().data)
+ .catch(this.handleError);
+ }
+ update(hero: Hero): Promise {
+ const url = `${this.heroesUrl}/${hero.id}`;
+ return this.http.put(url, JSON.stringify(hero), {headers: this.headers})
+ .toPromise()
+ .then(() => hero)
+ .catch(this.handleError);
+ }
+ delete(id: number): Promise {
+ const url = `${this.heroesUrl}/${id}`;
+ return this.http.delete(url, {headers: this.headers})
+ .toPromise()
+ .then(() => null)
+ .catch(this.handleError);
+ }
+
+ private handleError(error: any): Promise {
+ console.error('An error occurred', error); // for demo purposes only
+ return Promise.reject(error.message || error);
+ }
+}
diff --git a/app/hero.ts b/app/hero.ts
new file mode 100644
index 0000000..e3eac51
--- /dev/null
+++ b/app/hero.ts
@@ -0,0 +1,4 @@
+export class Hero {
+ id: number;
+ name: string;
+}
diff --git a/app/heroes.component.css b/app/heroes.component.css
new file mode 100644
index 0000000..cacc340
--- /dev/null
+++ b/app/heroes.component.css
@@ -0,0 +1,55 @@
+.selected {
+ background-color: #CFD8DC !important;
+ color: white;
+}
+.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.selected:hover {
+ background-color: #BBD8DC !important;
+ color: white;
+}
+.heroes li:hover {
+ color: #607D8B;
+ background-color: #DDD;
+ left: .1em;
+}
+.heroes .text {
+ position: relative;
+ top: -3px;
+}
+.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;
+ margin-right: .8em;
+ border-radius: 4px 0 0 4px;
+}
+
+button.delete {
+ float:right;
+ margin-top: 2px;
+ margin-right: .8em;
+ background-color: gray !important;
+ color:white;
+}
diff --git a/app/heroes.component.html b/app/heroes.component.html
new file mode 100644
index 0000000..cf0b311
--- /dev/null
+++ b/app/heroes.component.html
@@ -0,0 +1,20 @@
+My Heroes
+
+
+
+
+
+ -
+ {{hero.id}}
+ {{hero.name}}
+
+
+
+
+
+ {{selectedHero.name | uppercase}} is my hero
+
+
+
diff --git a/app/heroes.component.ts b/app/heroes.component.ts
new file mode 100644
index 0000000..d0b14a3
--- /dev/null
+++ b/app/heroes.component.ts
@@ -0,0 +1,56 @@
+import { Component, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+import { Hero } from './hero';
+import { HeroService } from './hero.service';
+
+@Component({
+ moduleId: module.id,
+ selector: 'my-heroes',
+ templateUrl: 'heroes.component.html',
+ styleUrls: [ 'heroes.component.css' ]
+})
+
+
+export class HeroesComponent implements OnInit {
+ heroes: Hero[];
+ selectedHero: Hero;
+
+ constructor(
+ private router: Router,
+ private heroService: HeroService
+ ) { }
+
+ getHeroes(): void {
+ this.heroService.getHeroes().then(heroes => this.heroes = heroes);
+ }
+
+ add(name: string): void {
+ name = name.trim();
+ if(!name) { return; }
+ this.heroService.create(name)
+ .then(hero => {
+ this.heroes.push(hero);
+ this.selectedHero = null;
+ });
+ }
+
+ delete(hero: Hero): void {
+ this.heroService.delete(hero.id)
+ .then(() => {
+ this.heroes = this.heroes.filter(h => h !== hero);
+ if (this.selectedHero === hero) { this.selectedHero = null; }
+ });
+ }
+
+ ngOnInit(): void {
+ this.getHeroes();
+ }
+
+ onSelect(hero: Hero): void {
+ this.selectedHero = hero;
+ }
+
+ gotoDetail(): void {
+ this.router.navigate(['/detail', this.selectedHero.id]);
+ }
+}
diff --git a/app/in-memory-data.service.ts b/app/in-memory-data.service.ts
new file mode 100644
index 0000000..0f50f26
--- /dev/null
+++ b/app/in-memory-data.service.ts
@@ -0,0 +1,18 @@
+import { InMemoryDbService } from 'angular-in-memory-web-api';
+export class InMemoryDataService implements InMemoryDbService {
+ createDb() {
+ let 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/index.html b/index.html
index 4c01792..616804a 100644
--- a/index.html
+++ b/index.html
@@ -1,5 +1,6 @@
+
Angular QuickStart