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:

results matching ""

    No results matching ""