<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>MyApp</title>
  <!--<base href="/">-->
  <script>document.write('<base href="' + document.location + '" />');</script>

  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="styles.css">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
    <script src="https://unpkg.com/core-js/client/shim.min.js"></script>
    <script src="https://unpkg.com/zone.js@0.7.4?main=browser"></script>
    <script src="https://unpkg.com/systemjs@0.19.39/dist/system.src.js"></script>
    <script src="systemjs.config.js"></script>
    <script>
      System.import('main.js').catch(function(err){ console.error(err); });
    </script>
</head>
<body>
  <app-root></app-root>
</body>
</html>
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';




platformBrowserDynamic().bootstrapModule(AppModule);


/*
Copyright 2017 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
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;
}
<h1>{{title}}</h1>
<nav>
	<a routerLink="/dashboard">ダッシュボード</a>
	<a routerLink="/heroines">ヒロインたち</a>
</nav>
<router-outlet></router-outlet>
<app-messages></app-messages>
import {Component} from '@angular/core';

@Component({
	selector: 'app-root',
	templateUrl: './app.component.html',
	styleUrls: ['./app.component.css']
})
export class AppComponent {
	title = 'ヒロイン一覧';
}
import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {HttpClientModule} from '@angular/common/http';
import {HttpClientInMemoryWebApiModule} from 'angular-in-memory-web-api';
import {InMemoryDataService}  from './in-memory-data.service';
import {AppComponent} from './app.component';
import {HeroinesComponent} from './heroines/heroines.component';
import {HeroineDetailComponent} from './heroine-detail/heroine-detail.component';
import {HeroineService} from './heroine.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 {HeroineSearchComponent} from './heroine-search/heroine-search.component';
@NgModule({
	declarations: [
		AppComponent,
		HeroinesComponent,
		HeroineDetailComponent,
		MessagesComponent,
		DashboardComponent,
		HeroineSearchComponent
	],
	imports: [
		BrowserModule,
		FormsModule,
		AppRoutingModule,
		HttpClientModule,
		HttpClientInMemoryWebApiModule.forRoot(
			InMemoryDataService, {dataEncapsulation: false}
		)
	  ],
	providers: [HeroineService, MessageService],
	bootstrap: [AppComponent]
})
export class AppModule {}
var templateUrlRegex = /templateUrl\s*:(\s*['"`](.*?)['"`]\s*)/gm;
var stylesRegex = /styleUrls *:(\s*\[[^\]]*?\])/g;
var stringRegex = /(['`"])((?:[^\\]\\\1|.)*?)\1/g;

module.exports.translate = function(load){
  if (load.source.indexOf('moduleId') != -1) return load;

  var url = document.createElement('a');
  url.href = load.address;

  var basePathParts = url.pathname.split('/');

  basePathParts.pop();
  var basePath = basePathParts.join('/');

  var baseHref = document.createElement('a');
  baseHref.href = this.baseURL;
  baseHref = baseHref.pathname;

  if (!baseHref.startsWith('/base/')) { // it is not karma
    basePath = basePath.replace(baseHref, '');
  }

  load.source = load.source
    .replace(templateUrlRegex, function(match, quote, url){
      var resolvedUrl = url;

      if (url.startsWith('.')) {
        resolvedUrl = basePath + url.substr(1);
      }

      return 'templateUrl: "' + resolvedUrl + '"';
    })
    .replace(stylesRegex, function(match, relativeUrls) {
      var urls = [];

      while ((match = stringRegex.exec(relativeUrls)) !== null) {
        if (match[2].startsWith('.')) {
          urls.push('"' + basePath + match[2].substr(1) + '"');
        } else {
          urls.push('"' + match[2] + '"');
        }
      }

      return "styleUrls: [" + urls.join(', ') + "]";
    });

  return load;
};
/**
 * WEB ANGULAR VERSION
 * (based on systemjs.config.js in angular.io)
 * System configuration for Angular samples
 * Adjust as necessary for your application needs.
 */
(function (global) {
  System.config({
    // DEMO ONLY! REAL CODE SHOULD NOT TRANSPILE IN THE BROWSER
    transpiler: 'ts',
    typescriptOptions: {
      // Copy of compiler options in standard tsconfig.json
      "target": "es5",
      "module": "commonjs",
      "moduleResolution": "node",
      "sourceMap": true,
      "emitDecoratorMetadata": true,
      "experimentalDecorators": true,
      "lib": ["es2015", "dom"],
      "noImplicitAny": true,
      "suppressImplicitAnyIndexErrors": true
    },
    meta: {
      'typescript': {
        "exports": "ts"
      }
    },
    paths: {
      // paths serve as alias
      'npm:': 'https://unpkg.com/'
    },
    // map tells the System loader where to look for things
    map: {
      // our app is within the app folder
      'app': 'app',

      // angular bundles
      '@angular/animations': 'npm:@angular/animations/bundles/animations.umd.js',
      '@angular/animations/browser': 'npm:@angular/animations/bundles/animations-browser.umd.js',
      '@angular/core': 'npm:@angular/core/bundles/core.umd.js',
      '@angular/common': 'npm:@angular/common/bundles/common.umd.js',
      '@angular/common/http': 'npm:@angular/common/bundles/common-http.umd.js',
      '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
      '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
      '@angular/platform-browser/animations': 'npm:@angular/platform-browser/bundles/platform-browser-animations.umd.js',
      '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
      '@angular/http': 'npm:@angular/http/bundles/http.umd.js',
      '@angular/router': 'npm:@angular/router/bundles/router.umd.js',
      '@angular/router/upgrade': 'npm:@angular/router/bundles/router-upgrade.umd.js',
      '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
      '@angular/upgrade': 'npm:@angular/upgrade/bundles/upgrade.umd.js',
      '@angular/upgrade/static': 'npm:@angular/upgrade/bundles/upgrade-static.umd.js',

      // other libraries
      'rxjs':                      'npm:rxjs@5.5.2',
      'rxjs/operators':            'npm:rxjs@5.5.2/operators/index.js',
      'tslib':                     'npm:tslib/tslib.js',
      'angular-in-memory-web-api': 'npm:angular-in-memory-web-api@0.4/bundles/in-memory-web-api.umd.js',
      'ts':                        'npm:plugin-typescript@5.2.7/lib/plugin.js',
      'typescript':                'npm:typescript@2.4.2/lib/typescript.js',

    },
    // packages tells the System loader how to load when no filename and/or no extension
    packages: {
      app: {
        main: './main.ts',
        defaultExtension: 'ts',
        meta: {
          './*.ts': {
            loader: 'systemjs-angular-loader.js'
          }
        }
      },
      rxjs: {
        defaultExtension: 'js'
      }
    }
  });

})(this);

/*
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
import {Component, OnInit} from '@angular/core';
import {Heroine} from '../heroine';
import {HeroineService} from '../heroine.service';
@Component({
	selector: 'app-heroines',
	templateUrl: './heroines.component.html',
	styleUrls: ['./heroines.component.css']
})
export class HeroinesComponent implements OnInit {
	heroines: Heroine[];
	constructor(private heroineService: HeroineService) {}
	ngOnInit() {
		this.getHeroines();
	}
	getHeroines(): void {
		this.heroineService.getHeroines()
		.subscribe(heroines => this.heroines = heroines);
	}
	add(name: string): void {
		name = name.trim();
		if (!name) {return;}
		// this.heroineService.addHeroine({name} as Heroine)
		this.heroineService.addHeroine({name} as Heroine, this.heroines.length)
		.subscribe(heroine =>
			this.heroines.push(heroine)
		);
	}
	delete(heroine: Heroine): void {
		this.heroines = this.heroines.filter(_heroine => _heroine !== heroine);
		this.heroineService.deleteHeroine(heroine).subscribe();
	}
}
<h2>ヒロインリスト</h2>
<div>
	<label>名前:
		<input #heroineName />
	</label>
	<button (click)="add(heroineName.value); heroineName.value=''">
		追加
	</button>
</div>
<ul class="heroines">
	<li *ngFor="let heroine of heroines">
		<a routerLink="/detail/{{heroine.id}}">
			<span class="badge">{{heroine.id}}</span> {{heroine.name}}
		</a>
		<button class="delete" title="データを削除"
		(click)="delete(heroine)">x</button>
	</li>
</ul>
export class Heroine {
	id: number;
	name: string;
}
/* 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;
}
.heroines {
    margin: 0 0 2em 0;
    list-style-type: none;
    padding: 0;
    width: 15em;
}
.heroines li {
    position: relative;
    cursor: pointer;
    background-color: #EEE;
    margin: .5em;
    padding: .3em 0;
    height: 1.6em;
    border-radius: 4px;
}
.heroines li:hover {
    color: #607D8B;
    background-color: #DDD;
    left: .1em;
}
.heroines a {
    color: #888;
    text-decoration: none;
    position: relative;
    display: block;
    width: 250px;
}
.heroines a:hover {
    color:#607D8B;
}
.heroines .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:hover {
	background-color: #cfd8dc;
}  
button.delete {
	position: relative;
	left: 194px;
	top: -32px;
	background-color: gray !important;
	color: white;
}
import {InMemoryDbService} from 'angular-in-memory-web-api';
export class InMemoryDataService implements InMemoryDbService {
	createDb() {
		const heroines = [
			{id: 11, name: 'シータ'},
			{id: 12, name: 'ナウシカ'},
			{id: 13, name: 'キキ'},
			{id: 14, name: '千尋'},
			{id: 15, name: 'さつき'},
			{id: 16, name: 'ソフィー'},
			{id: 17, name: 'マーニー'},
			{id: 18, name: '菜穂子'},
			{id: 19, name: 'サン'},
			{id: 20, name: 'フィオ'}
		];
		return {heroines};
	}
}
<div *ngIf="heroine">
	<h2>{{heroine.name}}の情報</h2>
	<div><span>番号: </span>{{heroine.id}}</div>
	<div>
		<label>名前:
			<input [(ngModel)]="heroine.name" placeholder="名前"/>
		</label>
	</div>
	<button (click)="goBack()">戻る</button>
	<button (click)="save()">保存</button>
</div>
import {Component, OnInit} from '@angular/core';
import {ActivatedRoute} from '@angular/router';
import {Location} from '@angular/common';
import {Heroine} from '../heroine';
import {HeroineService} from '../heroine.service';
@Component({
	selector: 'app-heroine-detail',
	templateUrl: './heroine-detail.component.html',
	styleUrls: ['./heroine-detail.component.css']
})
export class HeroineDetailComponent implements OnInit {
	heroine:Heroine;
	constructor(
		private route:ActivatedRoute,
		private heroineService:HeroineService,
		private location:Location
	) {}
	ngOnInit(): void {
		this.getHeroine();
	}
	getHeroine(): void {
		const id = +this.route.snapshot.paramMap.get('id');
		this.heroineService.getHeroine(id)
		.subscribe(heroine => this.heroine = heroine);
	}
	goBack(): void {
		this.location.back();
	}
	save(): void {
		this.heroineService.updateHeroine(this.heroine)
		.subscribe(() => this.goBack());
	}
}
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;
}
import {Injectable} from '@angular/core';
import {HttpClient, HttpHeaders} from '@angular/common/http';
import {Observable} from 'rxjs/Observable';
import {of} from 'rxjs/observable/of';
import {catchError, map, tap} from 'rxjs/operators';
import {Heroine} from './heroine';
import {MessageService} from './message.service';
const httpOptions = {
	headers: new HttpHeaders({'Content-Type': 'application/json'})
};
@Injectable()
export class HeroineService {
	private heroinesUrl = 'api/heroines';
	constructor(
		private http: HttpClient,
		private messageService: MessageService) {}
	getHeroines(): Observable<Heroine[]> {
		return this.http.get<Heroine[]>(this.heroinesUrl)
		.pipe(
			tap(heroines => this.log('データを取得')),
			catchError(this.handleError('getHeroines', []))
		);
	}
	getHeroine(id: number): Observable<Heroine> {
		const url = `${this.heroinesUrl}/${id}`;
		return this.http.get<Heroine>(url)
		.pipe(
			tap(_ => this.log(`番号${id}のデータを取得`)),
			catchError(this.handleError<Heroine>(`getHeroine 番号=${id}`))
		);
	}
	searchHeroines(term: string): Observable<Heroine[]> {
		if (!term.trim()) {
			return of([]);
		}
		const url = `${this.heroinesUrl}/?name=${term}`;
		return this.http.get<Heroine[]>(url)
		.pipe(
			tap(_ => this.log(`「${term}」のデータを検索`)),
			catchError(this.handleError<Heroine[]>('searchHeroines', []))
		);
	}
	addHeroine(heroine: Heroine, numHeroines: number): Observable<Heroine> {
		let heroineOvserbable;
		if (numHeroines === 0) {
			heroine.id = 11;
			heroineOvserbable = this.http.put(this.heroinesUrl, heroine, httpOptions)
		} else {
			heroineOvserbable = this.http.post<Heroine>(this.heroinesUrl, heroine, httpOptions);
		}
		return heroineOvserbable
		.pipe(
			tap((heroine: Heroine) => this.log(`番号${heroine.id}にデータを追加`)),
			catchError(this.handleError<Heroine>('addHeroine'))
		);
	}
	deleteHeroine (heroine: Heroine | number): Observable<Heroine> {
		const id = typeof heroine === 'number' ? heroine : heroine.id;
		const url = `${this.heroinesUrl}/${id}`;
		return this.http.delete<Heroine>(url, httpOptions)
		.pipe(
			tap(_ => this.log(`番号${id}のデータを削除`)),
			catchError(this.handleError<Heroine>('deleteHeroine'))
		);
	}
	updateHeroine(heroine: Heroine): Observable<any> {
		return this.http.put(this.heroinesUrl, heroine, httpOptions)
		.pipe(
			tap(_ => this.log(`番号${heroine.id}のデータを変更`)),
			catchError(this.handleError<any>('updateHeroine'))
		);
	}
	private handleError<T> (operation = 'operation', result?: T) {
		return (error: any): Observable<T> => {
			console.error(error);
			this.log(`${operation} failed: ${error.message}`);
			return of(result as T);
		};
	}
	private log(message: string) {
		this.messageService.add('HeroineService: ' + message);
	}
}
<div *ngIf="messageService.messages.length">
	<h2>メッセージ</h2>
	<button class="clear" (click)="messageService.clear()">消去</button>
	<div *ngFor='let message of messageService.messages'>{{message}}</div>
</div>
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() {
	}
}
import {Injectable} from '@angular/core';
@Injectable()
export class MessageService {
	messages: string[] = [];
	// constructor() {}
	add(message: string) {
		this.messages.push(message);
	}
	clear() {
		this.messages = [];
	}
}
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;
}
import {Component, OnInit} from '@angular/core';
import {Heroine} from '../heroine';
import {HeroineService} from '../heroine.service';
@Component({
	selector: 'app-dashboard',
	templateUrl: './dashboard.component.html',
	styleUrls: ['./dashboard.component.css']
})
export class DashboardComponent implements OnInit {
	heroines: Heroine[] = [];
	constructor(private heroineService: HeroineService) { }
	ngOnInit() {
		this.getHeroines();
	}
	getHeroines():void {
		this.heroineService.getHeroines()
		.subscribe(heroines => this.heroines = heroines.slice(0, 4));
	}
}
<h3>トップヒロイン</h3>
<div class="grid grid-pad">
	<a *ngFor="let heroine of heroines" class="col-1-4"
		routerLink="/detail/{{heroine.id}}">
			<div class="module heroine">
			<h4>{{heroine.name}}</h4>
		</div>
	</a>
</div>
<app-heroine-search></app-heroine-search>
[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;
	}
}
import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {DashboardComponent} from './dashboard/dashboard.component';
import {HeroinesComponent} from './heroines/heroines.component';
import {HeroineDetailComponent} from './heroine-detail/heroine-detail.component';
const routes: Routes = [
	{path: '', redirectTo: '/dashboard', pathMatch: 'full'},
	{path: 'dashboard', component: DashboardComponent},
	{path: 'detail/:id', component: HeroineDetailComponent},
	{path: 'heroines', component: HeroinesComponent}
];
@NgModule({
	imports: [RouterModule.forRoot(routes)],
	exports: [RouterModule]
})
export class AppRoutingModule {}
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 {Heroine} from '../heroine';
import {HeroineService} from '../heroine.service';
@Component({
	selector: 'app-heroine-search',
	templateUrl: './heroine-search.component.html',
	styleUrls: ['./heroine-search.component.css']
})
export class HeroineSearchComponent implements OnInit {
	heroines$: Observable<Heroine[]>;
	private searchTerms = new Subject<string>();
	constructor(private heroineService: HeroineService) {}
	search(term: string): void {
		this.searchTerms.next(term);
	}
	ngOnInit(): void {
		this.heroines$ = this.searchTerms
		.pipe(
			debounceTime(300),
			distinctUntilChanged(),
			switchMap((term: string) => this.heroineService.searchHeroines(term)),
		);
	}
}
<div id="search-component">
	<h4>ヒロイン検索</h4>
	<input #searchBox id="search-box" (keyup)="search(searchBox.value)" />  
	<ul class="search-result">
		<li *ngFor="let heroine of heroines$ | async" >
			<a routerLink="/detail/{{heroine.id}}">
				{{heroine.name}}
			</a>
		</li>
	</ul>
</div>
.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;
}