Angular

The following is a summary of the Angular Tower of Heroes tutorial –> “8. HTTP > Add a new hero (not included)”

Angular CLI

Install Angular CLI

npm install -g @angular/cli

Create a new application

ng new application-name

Serve the application and open the browser

cd application-name
ng serve --open

Note that the browser refreshes the page on any change.

Create a new component

ng generate component component-name

Create a new service

ng generate service service-name --module=app

The “–module=app” is optional and will cause the HeroService to be provided in the AppModule. It can be done manually later on by editing this file:

src/app/app.module.ts
...
@NgModule({
  ...
  providers: [
    ...,
    ServiceNameService
  ],
  ...
})
...

File structure

Application shell

  • src/app/app.component.ts : Class code (TypeScript)
  • src/app/app.component.html : Template (HTML)
  • src/app/app.component.css : Styles (CSS)

Component

  • src/app/component-name/component-name.component.ts : Class code (TypeScript)
  • src/app/component-name/component-name.component.html : Template (HTML)
  • src/app/component-name/component-name.component.css : Styles (CSS)

Service

  • src/app/service-name.service.ts : Class code (TypeScript)

Life cycle

Component

  • The method “ngOnInit()” is called after creating a component that implements “OnInit”

TypeScript

Custom class

src/app/hero.ts
export class Hero {
  id: number;
  name: string;
}

To create an instance of the class :

hero: Hero = {
    id: 1,
    name: 'Whatever'
};

Methods

Without return type

myMethod() {
  ...
}

With return type

myMethod(): SomeType {
  return ...;
}

With parameters

myMethod(message: string) {
  ...
}

Private

private myMethod() {
  ...
}

Array

Initialize

src/app/mock-heroes.ts
export const HEROES: Hero[] = [
  { id: 11, name: 'Mr. Nice' },
  { id: 12, name: 'Narco' },
  { id: 13, name: 'Bombasto' },
  { id: 14, name: 'Celeritas' },
  { id: 15, name: 'Magneta' },
  { id: 16, name: 'RubberMan' },
  { id: 17, name: 'Dynama' },
  { id: 18, name: 'Dr IQ' },
  { id: 19, name: 'Magma' },
  { id: 20, name: 'Tornado' }
];

Insert

someArray.push('something');

Contains

someArray.indexOf('something') > -1

Iterate

for (const entry of someArray) {
    // do something with entry
}

Import class or instances from another component

Class:

import { Hero } from '../hero';

Object:

import { HEROES } from '../mock-heroes';

Service:

import { HeroService } from '../hero.service';

Template literals

const example = `HeroService: fetched hero id=${id}`;

You can embed variables inside a String by using backticks (`).

Component template

{{hero}}

Include a component

<app-component-name></app-component-name>

“app-component-name” is the value of “selector” in the @Component of the model.

Include a component (while passing properties)

<app-component-name [hero]="selectedHero"></app-component-name>
src/app/component-name/component-name.component.ts
import { ..., Input } from '@angular/core';
...
export class ComponentNameComponent ... {
  @Input() hero: Hero;
}

Conditional print

<div *ngIf="hero">
   ...
</div>

Repeater directive

<ul>
  <li *ngFor="let hero of heroes">
    <span>{{hero.id}}</span> {{hero.name}}
  </li>
</ul>

Conditional style

<ul>
  <li *ngFor="let hero of heroes" [class.selected]="hero === selectedHero">
    ...
  </li>
</ul>

It gives the class “selected” to the <li> when the condition is matched.

Click event binding

src/app/heroes/heroes.component.html
<ul>
  <li *ngFor="let hero of heroes" (click)="onSelect(hero)">
    ...
  </li>
</ul>
src/app/heroes/heroes.component.ts
onSelect(hero: Hero): void {
  ...
}

Pipes

{{someString | uppercase}}

Converts the value of the “someString” property to uppercase.

Two-way binding between View and Model

src/app/component-name/component-name.component.html
<input [(ngModel)]="hero.name" placeholder="name">
src/app/app.module.ts
import { FormsModule } from '@angular/forms';
...
@NgModule({
...
  imports: [
    ...,
    FormsModule
  ],
...
})

Service injection

Edit the constructor like such :

import { SomeService } from '../some.service';
...
export class SomeComponent ... {
  constructor(private someService: SomeService) {}
  ...
}

Asynchronous programming

Observable

Some snippet of example code:

src/app/hero.service.ts
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
...
export class HeroService ... {
  ...
  getHeroes(): Observable<Hero[]> {
    return of(HEROES);
  }
}
src/app/heroes.component.ts
...
export class HeroesComponent ... {
  ...
  getHeroes(): void {
    this.heroService.getHeroes()
      .subscribe(heroes => this.heroes = heroes);
  }
  ...
  save(): void {
    this.heroService.updateHero(this.hero)
      .subscribe(() => this.goBack());
  }
}

Routing

Set up app routing

ng generate module app-routing --flat --module=app

It will end up in the following file : src/app/app-routing.module.ts

Edit it and put the following inside:

src/app/app-routing.module.ts
import { NgModule }             from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

const routes: Routes = [
];

@NgModule({
  exports: [ RouterModule ],
  imports: [ RouterModule.forRoot(routes) ]
})
export class AppRoutingModule {}

Print the output of the routing:

src/app/app.component.html
...
<router-outlet></router-outlet>
...

Simple route

src/app/app-routing.module.ts
import { HeroesComponent }      from './heroes/heroes.component';
...
const routes: Routes = [
  ...,
  { path: 'heroes', component: HeroesComponent }
];
...

When navigating to http://localhost:4200/heroes, we see the HeroesComponent.

Route with a parameter

src/app/app-routing.module.ts
import { HeroDetailComponent }      from './heroes/heroes-detail.component';
...
const routes: Routes = [
  ...,
  { path: 'detail/:id', component: HeroDetailComponent }
];
...
src/app/hero-detail/hero-detail.component.ts
import { ActivatedRoute } from '@angular/router';
...
export class HeroDetailComponent ... {
  constructor(
    private route: ActivatedRoute
  ) {}
  ...
  someMethod(): void {
    const id = +this.route.snapshot.paramMap.get('id');
    ...
  }

When navigating to http://localhost:4200/detail/13, we see the HeroDetailComponent with id=13. Note: Route parameters are always strings. The JavaScript (+) operator converts the string to a number, which is what a hero id should be.

Set a route for root

src/app/app-routing.module.ts
...
const routes: Routes = [
   { path: '', redirectTo: '/dashboard', pathMatch: 'full' },
   ...
];
...
src/app/app.component.html
...
<a routerLink="/heroes">Heroes</a>
<a *ngFor="let hero of heroes" routerLink="/detail/{{hero.id}}">
...

Back button

src/app/hero-detail/hero-detail.component.ts
import { Location } from '@angular/common';
...
export class HeroDetailComponent ... {
  constructor(
    private location: Location
  ) {}
  ...
  goBack(): void {
    this.location.back();
  }
}
src/app/app.component.html
<button (click)="goBack()">go back</button>

HTTP

Make HttpClient available

src/app/app.module.ts
import { HttpClientModule }    from '@angular/common/http';
...
@NgModule({
  ...,
  imports: [
    ...,
    HttpClientModule
  ]
})
export class AppModule { }
src/app/hero.service.ts
import { HttpClient } from '@angular/common/http';
...
export class HeroService {
  constructor(
    ...,
    private http: HttpClient) { }
  ...
}

Use HttpClient from a service

Get multiple values

src/app/hero.service.ts
...
export class HeroService {
  ...
  getHeroes() : Observable<Hero[]> {
    return this.http.get<Hero[]>('api/heroes');
  }
}

Get one value

src/app/hero.service.ts
...
export class HeroService {
  ...
  getHero(id: number): Observable<Hero> {
    const url = `api/heroes/${id}`;
    return this.http.get<Hero>(url);
  }
}

Update value

src/app/hero.service.ts
import { ..., HttpHeaders } from '@angular/common/http';
...
const httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
...
export class HeroService {
  ...
  updateHero (hero: Hero): Observable<any> {
    return this.http.put('api/heroes', hero, httpOptions);
  }
}

Catch exceptions

src/app/hero.service.ts
import { catchError } from 'rxjs/operators';
...
export class HeroService {
  ...
  getHeroes() : Observable<Hero[]> {
    return this.http.get<Hero[]>('api/heroes').pipe(
      catchError(this.handleError('getHeroes', []))
    );
  }
  
  /**
   * Handle Http operation that failed.
   * Let the app continue.
   * @param operation - name of the operation that failed
   * @param result - optional value to return as the observable result
   */
  private handleError<T> (operation = 'operation', result?: T) {
    return (error: any): Observable<T> => {
      // Let the app keep running by returning an empty result.
      return of(result as T);
    };
  }
}

Tap into values

If you want to quickly do something (like logging) without touching at the observable values.

src/app/hero.service.ts
import { tap } from 'rxjs/operators';
...
export class HeroService {
  ...
  getHeroes() : Observable<Hero[]> {
    return this.http.get<Hero[]>('api/heroes').pipe(
      tap(heroes => this.log(`fetched heroes`)),
      ...
    );
  }
}
Print/export