29. Tour Of Technologies Tutorial
package.json:
{
"name": "angular5-tour-of-technologies",
"version": "0.0.0",
"license": "MIT",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "^5.0.0",
"@angular/common": "^5.0.0",
"@angular/compiler": "^5.0.0",
"@angular/core": "^5.0.0",
"@angular/forms": "^5.0.0",
"@angular/http": "^5.0.0",
"@angular/platform-browser": "^5.0.0",
"@angular/platform-browser-dynamic": "^5.0.0",
"@angular/platform-server": "^5.0.0",
"@angular/router": "^5.0.0",
"core-js": "^2.4.1",
"rxjs": "^5.5.2",
"zone.js": "^0.8.14"
},
"devDependencies": {
"@angular/cli": "^1.5.0",
"@angular/compiler-cli": "^5.0.0",
"@angular/language-service": "^4.2.4",
"@types/jasmine": "~2.5.53",
"@types/jasminewd2": "~2.0.2",
"@types/node": "~6.0.60",
"codelyzer": "~3.1.1",
"jasmine-core": "~2.6.2",
"jasmine-spec-reporter": "~4.1.0",
"karma": "~1.7.0",
"karma-chrome-launcher": "~2.1.1",
"karma-cli": "~1.0.1",
"karma-coverage-istanbul-reporter": "^1.2.1",
"karma-jasmine": "~1.1.0",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.1.2",
"ts-node": "~3.2.0",
"tslint": "~5.3.2",
"typescript": "2.4.2"
}
}
AppComponent:
import { Component } from '@angular/core';
export class Technology {
id: number;
name: string;
}
@Component({
selector: 'app-root',
template: `
<div class ="container">
<div class="left">
<h1>{{ title }}</h1>
<h2>{{ technology.name }} details!</h2>
<div><label>id: </label>{{ technology.id }}</div>
<div><label>name: </label><input [(ngModel)]="technology.name" placeholder="name"></div>
</div>
<div class="right">
<img class="img-round" [src]="imageUrl" alt="princess image">
</div>
</div>
`
})
export class AppComponent {
imageUrl = '../assets/polymer5.jpg';
title = 'Tour Of Technologies';
technology: Technology = {
id: 1,
name: 'Angular 5'
};
}
AppModule:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
main.ts:
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);
index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>ng5-tour-of-technologies</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>
styles.css:
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;
padding: 20px;
}
body, input[text], button {
color: #888;
font-family: Cambria, Georgia;
}
a {
cursor: pointer;
cursor: hand;
}
button {
font-family: Arial;
color: white;
background-color: #ff4d4d;
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
cursor: hand;
}
button:hover {
color: #fff;
background-color: #ff3333;
}
button:disabled {
background-color: #eee;
color: #aaa;
cursor: auto;
}
/* Navigation link styles */
nav a {
padding: 5px 10px;
text-decoration: none;
margin-top: 10px;
display: inline-block;
color: #fff;
background-color: #cc0000;
border-radius: 4px;
}
nav a:visited, a:link {
color: #fff;
}
nav a:hover {
color: #fff;
background-color: #ff3333;
}
nav a.active {
background-color: #ff3333;
}
.left {
float: left;
}
.right {
float: right;
}
.img-round {
height: 600px;
width: 600px;
border-radius: 100%;
}
/* everywhere else */
* {
font-family: Arial, Helvetica, sans-serif;
}
/*
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
*/
Let's view the results in our Chromium Web Browser:
AppComponent Master/Detail:
import { Component } from '@angular/core';
export class Technology {
id: number;
name: string;
}
const TECHNOLOGIES: Technology[] = [
{ id: 11, name: 'Angular 5' },
{ id: 12, name: 'Angular CLI 1.5' },
{ id: 13, name: 'Angular Material 2' },
{ id: 14, name: 'Angular Universal' },
{ id: 15, name: 'Modules' },
{ id: 16, name: 'Components' },
{ id: 17, name: 'Templates' },
{ id: 18, name: 'Metadata' },
{ id: 19, name: 'Data Binding' },
{ id: 20, name: 'Directives' },
{ id: 21, name: 'Services' },
{ id: 22, name: 'Dependency Injection' }
];
@Component({
selector: 'app-root',
template: `
<div class ="container">
<div class="left">
<h1>{{ title }}</h1>
<h2>My Technologies</h2>
<ul class="technologies">
<li *ngFor="let technology of technologies"
(click)="onSelect(technology)"
[class.selected]="technology === selectedTechnology">
<span class="badge">{{ technology.id }}</span> {{ technology.name }}
</li>
</ul>
<div *ngIf="selectedTechnology">
<h2>{{ selectedTechnology.name }} details!</h2>
<div><label>id: </label>{{ selectedTechnology.id }}</div>
<div><label>name: </label><input [(ngModel)]="selectedTechnology.name"
placeholder="name"></div>
</div>
</div>
<div class="right">
<img class="img-round" [src]="imageUrl" alt="princess image">
</div>
</div>
`,
styles: [`
.selected {
background-color: #ff1a1a !important;
color: white;
}
.technologies {
margin: 0 0 2em 0;
list-style-type: none;
padding: 0;
width: 15em;
}
.technologies li {
cursor: pointer;
position: relative;
left: 0;
color: white;
background-color: #ff8080;
margin: .5em;
padding: .3em 0;
height: 1.6em;
border-radius: 4px;
}
.technologies li.selected:hover{
background-color: #ff3333 !important;
color: white;
}
.technologies li:hover {
color: white;
background-color: #ff4d4d;
left: .1em;
}
.technologies .text {
position: relative;
top: -3px;
}
.technologies .badge {
display: inline-block;
font-size: small;
color: white;
padding: 0.8em 0.7em 0 0.7em;
background-color: #cc0000;
line-height: 1em;
position: relative;
left: -1px;
top: -4px;
height: 1.8em;
margin-right: .8em;
border-radius: 4px 0 0 4px;
}
`]
})
export class AppComponent {
imageUrl = '../assets/polymer5.jpg';
title = 'Tour Of Technologies';
technologies = TECHNOLOGIES;
selectedTechnology: Technology;
onSelect(technology: Technology): void {
this.selectedTechnology = technology;
}
}
Let's look at the app in our Chromium Browser:
TechnologyDetailComponent Multiple Components:
import { Component, Input } from '@angular/core';
import { Technology } from './technology';
@Component({
selector: 'app-technology-detail',
template: `
<div *ngIf="technology">
<h2>{{ technology.name }} details!</h2>
<div><label>id: </label>{{ technology.id }}</div>
<div><label>name: </label><input [(ngModel)]="technology.name"
placeholder="name"></div>
</div>
`
})
export class TechnologyDetailComponent {
@Input() technology: Technology;
}
AppComponent Multiple Components:
import { Component } from '@angular/core';
import { Technology } from './technology';
const TECHNOLOGIES: Technology[] = [
{ id: 11, name: 'Angular 5' },
{ id: 12, name: 'Angular CLI 1.5' },
{ id: 13, name: 'Angular Material 2' },
{ id: 14, name: 'Angular Universal' },
{ id: 15, name: 'Modules' },
{ id: 16, name: 'Components' },
{ id: 17, name: 'Templates' },
{ id: 18, name: 'Metadata' },
{ id: 19, name: 'Data Binding' },
{ id: 20, name: 'Directives' },
{ id: 21, name: 'Services' },
{ id: 22, name: 'Dependency Injection' }
];
@Component({
selector: 'app-root',
template: `
<div class ="container">
<div class="left">
<h1>{{ title }}</h1>
<h2>My Technologies</h2>
<ul class="technologies">
<li *ngFor="let technology of technologies"
(click)="onSelect(technology)"
[class.selected]="technology === selectedTechnology">
<span class="badge">{{ technology.id }}</span> {{ technology.name }}
</li>
</ul>
<app-technology-detail [technology]="selectedTechnology"></app-technology-detail>
</div>
<div class="right">
<img class="img-round" [src]="imageUrl" alt="princess image">
</div>
</div>
`,
styles: [`
.selected {
background-color: #ff1a1a !important;
color: white;
}
.technologies {
margin: 0 0 2em 0;
list-style-type: none;
padding: 0;
width: 15em;
}
.technologies li {
cursor: pointer;
position: relative;
left: 0;
color: white;
background-color: #ff8080;
margin: .5em;
padding: .3em 0;
height: 1.6em;
border-radius: 4px;
}
.technologies li.selected:hover{
background-color: #ff3333 !important;
color: white;
}
.technologies li:hover {
color: white;
background-color: #ff4d4d;
left: .1em;
}
.technologies .text {
position: relative;
top: -3px;
}
.technologies .badge {
display: inline-block;
font-size: small;
color: white;
padding: 0.8em 0.7em 0 0.7em;
background-color: #cc0000;
line-height: 1em;
position: relative;
left: -1px;
top: -4px;
height: 1.8em;
margin-right: .8em;
border-radius: 4px 0 0 4px;
}
`]
})
export class AppComponent {
imageUrl = '../assets/polymer5.jpg';
title = 'Tour Of Technologies';
technologies = TECHNOLOGIES;
selectedTechnology: Technology;
onSelect(technology: Technology): void {
this.selectedTechnology = technology;
}
}
Technology Class Multiple Components:
export class Technology {
id: number;
name: string;
}
AppModule Multiple Components:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { TechnologyDetailComponent } from './technology-detail.component';
@NgModule({
declarations: [
AppComponent,
TechnologyDetailComponent
],
imports: [
BrowserModule,
FormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Let's have a look at the results in our Chromium Web Browser:
TechnologyService Services:
import { Injectable } from '@angular/core';
import { Technology } from './technology';
import { TECHNOLOGIES } from './mock-technologies';
@Injectable()
export class TechnologyService {
getTechnologies(): Promise<Technology[]> {
return Promise.resolve(TECHNOLOGIES);
}
getTechnologiesSlowly(): Promise<Technology[]> {
return new Promise(resolve => {
// simulate server latency with two second delay
setTimeout(() => resolve(this.getTechnologies()), 2000);
});
}
}
AppComponent Services:
import { Component, OnInit } from '@angular/core';
import { Technology } from './technology';
import { TechnologyService } from './technology.service';
@Component({
selector: 'app-root',
template: `
<div class ="container">
<div class="left">
<h1>{{ title }}</h1>
<h2>My Technologies</h2>
<ul class="technologies">
<li *ngFor="let technology of technologies"
(click)="onSelect(technology)"
[class.selected]="technology === selectedTechnology">
<span class="badge">{{ technology.id }}</span> {{ technology.name }}
</li>
</ul>
<app-technology-detail [technology]="selectedTechnology"></app-technology-detail>
</div>
<div class="right">
<img class="img-round" [src]="imageUrl" alt="princess image">
</div>
</div>
`,
styles: [`
.selected {
background-color: #ff1a1a !important;
color: white;
}
.technologies {
margin: 0 0 2em 0;
list-style-type: none;
padding: 0;
width: 15em;
}
.technologies li {
cursor: pointer;
position: relative;
left: 0;
color: white;
background-color: #ff8080;
margin: .5em;
padding: .3em 0;
height: 1.6em;
border-radius: 4px;
}
.technologies li.selected:hover{
background-color: #ff3333 !important;
color: white;
}
.technologies li:hover {
color: white;
background-color: #ff4d4d;
left: .1em;
}
.technologies .text {
position: relative;
top: -3px;
}
.technologies .badge {
display: inline-block;
font-size: small;
color: white;
padding: 0.8em 0.7em 0 0.7em;
background-color: #cc0000;
line-height: 1em;
position: relative;
left: -1px;
top: -4px;
height: 1.8em;
margin-right: .8em;
border-radius: 4px 0 0 4px;
}
`],
providers: [ TechnologyService ]
})
export class AppComponent implements OnInit {
imageUrl = '../assets/polymer5.jpg';
title = 'Tour Of Technologies';
technologies: Technology[];
selectedTechnology: Technology;
constructor(private technologyService: TechnologyService) { }
ngOnInit(): void {
this.getTechnologies();
}
getTechnologies() {
this.technologyService.getTechnologies()
.then(technologies => this.technologies = technologies);
}
onSelect(technology: Technology): void {
this.selectedTechnology = technology;
}
}
TECHNOLOGIES Services:
import { Technology } from './technology';
export const TECHNOLOGIES: Technology[] = [
{ id: 11, name: 'Angular 5' },
{ id: 12, name: 'Angular CLI 1.5' },
{ id: 13, name: 'Angular Material 2' },
{ id: 14, name: 'Angular Universal' },
{ id: 15, name: 'Modules' },
{ id: 16, name: 'Components' },
{ id: 17, name: 'Templates' },
{ id: 18, name: 'Metadata' },
{ id: 19, name: 'Data Binding' },
{ id: 20, name: 'Directives' },
{ id: 21, name: 'Services' },
{ id: 22, name: 'Dependency Injection' }
];
AppRoutingModule Routing:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { DashboardComponent } from './dashboard.component';
import { TechnologiesComponent } from './technologies.component';
import { TechnologyDetailComponent } from './technology-detail.component';
const routes: Routes = [
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
{ path: 'dashboard', component: DashboardComponent },
{ path: 'detail/:id', component: TechnologyDetailComponent },
{ path: 'technologies', component: TechnologiesComponent }
];
@NgModule({
imports: [ RouterModule.forRoot(routes) ],
exports: [ RouterModule ]
})
export class AppRoutingModule { }
AppComponent Routing:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<div class="container">
<div class="left">
<h1>{{ title }}</h1>
<nav>
<a routerLink="/dashboard" routerLinkActive="active">Dashboard</a>
<a routerLink="/technologies" routerLinkActive="active">Technologies</a>
</nav>
<router-outlet></router-outlet>
</div>
<div class="right">
<img class="img-round" [src]="imageUrl" alt="princess image">
</div>
</div>
`
})
export class AppComponent {
imageUrl = '../assets/polymer5.jpg';
title = 'Tour Of Technologies';
}
AppModule Routing:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { TechnologiesComponent } from './technologies.component';
import { TechnologyDetailComponent } from './technology-detail.component';
import { DashboardComponent } from './dashboard.component';
import { TechnologyService } from './technology.service';
@NgModule({
declarations: [
AppComponent,
TechnologiesComponent,
TechnologyDetailComponent,
DashboardComponent
],
imports: [
BrowserModule,
FormsModule,
AppRoutingModule
],
providers: [ TechnologyService ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
DashboardComponent Routing:
import { Component, OnInit } from '@angular/core';
import { Technology } from './technology';
import { TechnologyService } from './technology.service';
@Component({
selector: 'app-dashboard',
template: `
<h3>Top Technologies</h3>
<div class="grid grid-pad">
<a *ngFor="let technology of technologies" [routerLink]="['/detail', technology.id]"
class="col-1-4">
<div class="module technology">
<h4>{{ technology.name }}</h4>
</div>
</a>
</div>
`,
styles: [`
[class*='col-1'] {
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;
}
h2 {
font-size: 2em;
color: #999;
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;
background-color: #ff8080;
max-height: 100px;
max-width: 120px;
border-radius: 2px;
}
.module:hover {
color: #eee;
cursor: pointer;
background-color: #ff4d4d;
}
.grid-pad {
padding: 10px 0;
}
.grid-pad > [class*='col-1']: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;
}
}
`]
})
export class DashboardComponent implements OnInit {
technologies: Technology[] = [];
constructor(private technologyService: TechnologyService) { }
ngOnInit() {
this.getTechnologies();
}
getTechnologies() {
this.technologyService.getTechnologies()
.then(technologies => this.technologies = technologies.slice(0, 4));
}
}
TechnologiesComponent Routing:
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Technology } from './technology';
import { TechnologyService } from './technology.service';
@Component({
selector: 'app-technologies',
template: `
<h2>My Technologies</h2>
<ul class="technologies">
<li *ngFor="let technology of technologies"
(click)="onSelect(technology)"
[class.selected]="technology === selectedTechnology">
<span class="badge">{{ technology.id }}</span> {{ technology.name }}
</li>
</ul>
<div *ngIf="selectedTechnology">
<h2>
{{ selectedTechnology.name | uppercase }} is my technology
</h2>
<button (click)="gotoDetail()">View Details</button>
</div>
`,
styles: [`
.selected {
background-color: #ff1a1a !important;
color: white;
}
.technologies {
margin: 0 0 2em 0;
list-style-type: none;
padding: 0;
width: 15em;
}
.technologies li {
cursor: pointer;
position: relative;
left: 0;
color: white;
background-color: #ff8080;
margin: .5em;
padding: .3em 0;
height: 1.6em;
border-radius: 4px;
}
.technologies li.selected:hover{
background-color: #ff3333 !important;
color: white;
}
.technologies li:hover {
color: white;
background-color: #ff4d4d;
left: .1em;
}
.technologies .text {
position: relative;
top: -3px;
}
.technologies .badge {
display: inline-block;
font-size: small;
color: white;
padding: 0.8em 0.7em 0 0.7em;
background-color: #cc0000;
line-height: 1em;
position: relative;
left: -1px;
top: -4px;
height: 1.8em;
margin-right: .8em;
border-radius: 4px 0 0 4px;
}
button {
font-family: Arial;
color: white;
background-color: #ff4d4d;
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
cursor: hand;
}
button:hover {
background-color: #ff3333;
}
`]
})
export class TechnologiesComponent implements OnInit {
technologies: Technology[];
selectedTechnology: Technology;
constructor(private technologyService: TechnologyService,
private router: Router) { }
ngOnInit(): void {
this.getTechnologies();
}
getTechnologies() {
this.technologyService.getTechnologies()
.then(technologies => this.technologies = technologies);
}
onSelect(technology: Technology): void {
this.selectedTechnology = technology;
}
gotoDetail() {
this.router.navigate(['/detail', this.selectedTechnology.id]);
}
}
TechnologyDetailComponent Routing:
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, ParamMap } from '@angular/router';
import { Location } from '@angular/common';
import { Technology } from './technology';
import { TechnologyService } from './technology.service';
import 'rxjs/add/operator/switchMap';
@Component({
selector: 'app-technology-detail',
template: `
<div *ngIf="technology">
<h2>{{ technology.name }} details!</h2>
<div><label>id: </label>{{ technology.id }}</div>
<div><label>name: </label>
<input [(ngModel)]="technology.name" placeholder="name">
</div>
<button (click)="goBack()">Go Back</button>
</div>
`,
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 {
font-family: Arial;
color: white;
background-color: #ff4d4d;
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
cursor: hand;
}
button:hover {
color: #fff;
background-color: #ff3333;
}
button:disabled {
background-color: #eee;
color: #aaa;
cursor: auto;
}
`]
})
export class TechnologyDetailComponent implements OnInit {
technology: Technology;
constructor(private technologyService: TechnologyService,
private route: ActivatedRoute,
private location: Location) { }
ngOnInit(): void {
this.route.paramMap
.switchMap((params: ParamMap) => this.technologyService.getTechnology(+params.get('id')))
.subscribe(technology => this.technology = technology);
}
goBack(): void {
this.location.back();
}
}
TechnologyService Routing:
import { Injectable } from '@angular/core';
import { Technology } from './technology';
import { TECHNOLOGIES } from './mock-technologies';
@Injectable()
export class TechnologyService {
getTechnologies(): Promise<Technology[]> {
return Promise.resolve(TECHNOLOGIES);
}
getTechnology(id: number): Promise<Technology> {
return this.getTechnologies()
.then(technologies => technologies.find(technology => technology.id === id));
}
getTechnologiesSlowly(): Promise<Technology[]> {
return new Promise(resolve => {
// simulate server latency with two second delay
setTimeout(() => resolve(this.getTechnologies()), 2000);
});
}
}
Technology Class Routing:
export class Technology {
id: number;
name: string;
}
Let's view the Routes in our Chromium Browser:
AppComponent HTTP:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<div class="container">
<div class="left">
<h1>{{ title }}</h1>
<nav>
<a routerLink="/dashboard" routerLinkActive="active">Dashboard</a>
<a routerLink="/technologies" routerLinkActive="active">Technologies</a>
</nav>
<router-outlet></router-outlet>
</div>
<div class="right">
<img class="img-round" [src]="imageUrl" alt="princess image">
</div>
</div>
`
})
export class AppComponent {
imageUrl = '../assets/polymer5.jpg';
title = 'Tour Of Technologies';
}
AppModule HTTP:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppRoutingModule } from './app-routing.module';
import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
import { InMemoryDataService } from './in-memory-data.service';
import { AppComponent } from './app.component';
import { TechnologiesComponent } from './technologies.component';
import { TechnologyDetailComponent } from './technology-detail.component';
import { DashboardComponent } from './dashboard.component';
import { TechnologySearchComponent } from './technology-search.component';
import { TechnologyService } from './technology.service';
import { TechnologySearchService } from './technology-search.service';
@NgModule({
declarations: [
AppComponent,
TechnologiesComponent,
TechnologyDetailComponent,
DashboardComponent,
TechnologySearchComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
InMemoryWebApiModule.forRoot(InMemoryDataService),
AppRoutingModule
],
providers: [ TechnologyService, TechnologySearchService ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
TechnologiesComponent HTTP:
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Technology } from './technology';
import { TechnologyService } from './technology.service';
@Component({
selector: 'app-technologies',
template: `
<h2>My Technologies</h2>
<div>
<label>Technology name:</label><input #technology />
<button (click)="add(technology.value)">Add</button>
</div>
<ul class="technologies">
<li *ngFor="let technology of technologies"
(click)="onSelect(technology)"
[class.selected]="technology === selectedTechnology">
<span class="badge">{{ technology.id }}</span> {{ technology.name }}
<button class="delete"
(click)="delete(technology); $event.stopPropagation();">
x
</button>
</li>
</ul>
<div *ngIf="selectedTechnology">
<h2>
{{ selectedTechnology.name | uppercase }} is my technology
</h2>
<button (click)="gotoDetail()">View Details</button>
</div>
`,
styles: [`
.selected {
background-color: #ff1a1a !important;
color: white;
}
.technologies {
margin: 0 0 2em 0;
list-style-type: none;
padding: 0;
width: 24em;
}
.technologies li {
cursor: pointer;
position: relative;
left: 0;
color: white;
background-color: #ff8080;
margin: .5em;
padding: .3em 0;
height: 1.6em;
border-radius: 4px;
}
.technologies li.selected:hover{
background-color: #ff3333 !important;
color: white;
}
.technologies li:hover {
color: white;
background-color: #ff4d4d;
left: .1em;
}
.technologies .text {
position: relative;
top: -3px;
}
.technologies .badge {
display: inline-block;
font-size: small;
color: white;
padding: 0.8em 0.7em 0 0.7em;
background-color: #cc0000;
line-height: 1em;
position: relative;
left: -1px;
top: -4px;
height: 1.8em;
width: 1em;
margin-right: .8em;
border-radius: 4px 0 0 4px;
}
button {
font-family: Arial;
color: white;
background-color: #ff4d4d;
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
cursor: hand;
}
button:hover {
background-color: #ff3333;
}
button.delete {
float: right;
margin-top: 2px;
margin-right: .8em;
background-color: #ff3333 !important;
color: white;
}
`]
})
export class TechnologiesComponent implements OnInit {
technologies: Technology[];
selectedTechnology: Technology;
constructor(private technologyService: TechnologyService,
private router: Router) { }
ngOnInit(): void {
this.getTechnologies();
}
getTechnologies() {
this.technologyService.getTechnologies()
.then(technologies => this.technologies = technologies);
}
add(name: string): void {
name = name.trim();
if (!name) { return; }
this.technologyService.create(name).then(technology => {
this.technologies.push(technology);
this.selectedTechnology = null;
});
}
delete(technology: Technology): void {
this.technologies = this.technologies.filter(t => t.id !== technology.id);
if (this.selectedTechnology === technology) { this.selectedTechnology = null; }
this.technologyService.delete(technology.id).then(() => null);
}
onSelect(technology: Technology): void {
this.selectedTechnology = technology;
}
gotoDetail() {
this.router.navigate(['/detail', this.selectedTechnology.id]);
}
}
TechnologyDetailComponent HTTP:
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, ParamMap } from '@angular/router';
import { Location } from '@angular/common';
import { Technology } from './technology';
import { TechnologyService } from './technology.service';
import 'rxjs/add/operator/switchMap';
@Component({
selector: 'app-technology-detail',
template: `
<div *ngIf="technology">
<h2>{{ technology.name }} details!</h2>
<div><label>id: </label>{{ technology.id }}</div>
<div><label>name: </label>
<input [(ngModel)]="technology.name" placeholder="name">
</div>
<button (click)="save()">Save</button>
<button (click)="goBack()">Go Back</button>
</div>
`,
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 {
font-family: Arial;
color: white;
background-color: #ff4d4d;
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
cursor: hand;
}
button:hover {
color: #fff;
background-color: #ff3333;
}
button:disabled {
background-color: #eee;
color: #aaa;
cursor: auto;
}
`]
})
export class TechnologyDetailComponent implements OnInit {
technology: Technology;
constructor(private technologyService: TechnologyService,
private route: ActivatedRoute,
private location: Location) { }
ngOnInit(): void {
this.route.paramMap
.switchMap((params: ParamMap) => this.technologyService.getTechnology(+params.get('id')))
.subscribe(technology => this.technology = technology);
}
save(): void {
this.technologyService.update(this.technology).then(() => this.goBack());
}
goBack(): void {
this.location.back();
}
}
TechnologyService HTTP:
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { Technology } from './technology';
import 'rxjs/add/operator/toPromise';
@Injectable()
export class TechnologyService {
private headers = new Headers({'Content-Type': 'application/json'});
private technologiesURL = 'api/technologies';
constructor(private http: Http) { }
create(name: string): Promise<Technology> {
return this.http.post(this.technologiesURL, JSON.stringify({name: name}))
.toPromise()
.then(response => response.json() as Technology)
.catch(this.handleError);
}
getTechnologies(): Promise<Technology[]> {
return this.http.get(this.technologiesURL)
.toPromise()
.then(response => response.json() as Technology[])
.catch(this.handleError);
}
getTechnology(id: number): Promise<Technology> {
const url = `${this.technologiesURL}/${id}`;
return this.http.get(url)
.toPromise()
.then(response => response.json() as Technology)
.catch(this.handleError);
}
update(technology: Technology): Promise<Technology> {
const url = `${this.technologiesURL}/${technology.id}`;
return this.http.put(url, JSON.stringify(technology))
.toPromise()
.then(() => technology)
.catch(this.handleError);
}
delete(id: number): Promise<void> {
const url = `${this.technologiesURL}/${id}`;
return this.http.delete(url)
.toPromise()
.then(() => null)
.catch(this.handleError);
}
private handleError(error: any): Promise<any> {
console.error('An error occured', error);
return Promise.reject(error.message || error);
}
getTechnologiesSlowly(): Promise<Technology[]> {
return new Promise(resolve => {
// simulate server latency with two second delay
setTimeout(() => resolve(this.getTechnologies()), 2000);
});
}
}
InMemoryDataService HTTP:
import { InMemoryDbService } from 'angular-in-memory-web-api';
export class InMemoryDataService implements InMemoryDbService {
createDb() {
const technologies = [
{ id: 0, name: 'Ionic 3' },
{ id: 11, name: 'Angular 5' },
{ id: 12, name: 'Angular CLI 1.5' },
{ id: 13, name: 'Angular Material 2' },
{ id: 14, name: 'Angular Universal' },
{ id: 15, name: 'Modules' },
{ id: 16, name: 'Components' },
{ id: 17, name: 'Templates' },
{ id: 18, name: 'Metadata' },
{ id: 19, name: 'Data Binding' },
{ id: 20, name: 'Directives' },
{ id: 21, name: 'Services' },
{ id: 22, name: 'Dependency Injection' }
];
return { technologies };
}
}
TechnologySearchService HTTP:
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import { Technology } from './technology';
@Injectable()
export class TechnologySearchService {
private technologiesURL = 'api/technologies';
constructor(private http: Http) { }
search(term: string) {
return this.http.get(`${this.technologiesURL}/?name=${term}`)
.map(response => response.json() as Technology[]);
}
}
TechnologySearchComponent HTTP:
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Technology } from './technology';
import { TechnologySearchService } from './technology-search.service';
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 'rxjs/add/operator/switchMap';
@Component({
selector: 'app-technology-search',
template: `
<div id="search-component">
<h4>Technology Search</h4>
<input #searchBox id="search-box" (keyup)="search(searchBox.value)" />
<div>
<div *ngFor="let technology of technologies | async" (click)="gotoDetail(technology)"
class="search-result">
{{ technology.name }}
</div>
</div>
</div>
`,
styles: [`
.search-result {
border-bottom: 1px solid red;
border-left: 1px solid red;
border-right: 1px solid red;
width: 192px;
height: 16px;
padding: 5px;
background-color: white;
cursor: pointer;
}
.search-result:hover {
color: #eee;
background-color: #ff8080;
}
#search-box {
width: 200px;
height: 20px;
}
`]
})
export class TechnologySearchComponent implements OnInit {
technologies: Observable<Technology[]>;
private searchTerms = new Subject<string>();
constructor(private router: Router,
private technologySearchService: TechnologySearchService) { }
// push a search term into observable stream
search(term: string): void {
this.searchTerms.next(term);
}
ngOnInit(): void {
this.technologies = this.searchTerms
.debounceTime(300)
.distinctUntilChanged()
.switchMap(term =>
term ? this.technologySearchService.search(term)
: Observable.of<Technology[]>([]))
.catch(error => {
console.log(error);
return Observable.of<Technology[]>([]);
});
}
gotoDetail(technology: Technology): void {
const link = ['/detail', technology.id];
this.router.navigate(link);
}
}
Let's view the results in our Chromium Web Brother: