Angular + Typescript Notes

Ultimate Course

Typescript: A type checking wrapper on top of javascript

Property binding

both of them are updated dynamically.


name=”<plain string> , or {{interpolation}} as string”

Event binding

<input (input)="onChange($event)">

Two way binding

<input [ngModel]="name" (ngModelChange)="handleChange($event)">

<input [(ngModel)="name"] > // internally, angular registers an event handler and update name for you.

Template Ref

<input #inputElem>  // This is a template ref, the id for it is inputElem
<button (click)="onClick(inputElem.value)">click me</button> // pass value of inputElem to the function

Rendering flow

ngIf, * syntax and <ng-template>

<div *ngIf="<expression>">

is equal to

<ng-template [ngIf]="name.length > 0">
  <p>searching for {{name}}...</p>

The “*” is a syntax sugar.


<div *ngFor="let item of items; let i = index;"> {{i}} : {{item}}</div>

*ngFor is a syntax sugar, and the code block above is equivalent to

<ng-template ngFor let-i="index" let-item [ngForOf]="items">
  <div> {{i}} : {{item}} </div>

ngFor is a structure directive

You can also use ngFor on a single layer of an element.

<div *ngFor="let item of items" [name]="item"></div>

ngClass and className bindings

[class.className]=”expression” and [ngClass]=”{ ‘className’: expression, ‘anotherClassName’: anotherExpression}” can both add className to the element, but ngClass can add multiple classNames to it.

<div [class.checked-in]="isCheckedIn"> // "checked-in" class is added to the element if "isCheckedIn" is evaluated to true

<div [ngClass]=" { 'checked-in': isCheckedIn, 'checked-out': 'isCheckedOut'}"> // 'checked-in' is added to the element if 'isCheckedIn' is evaluated to true; 'checked-out' is added to the element if isCheckedOut is evaluated to true. 

ngStyle and style bindings

<div [style.background]="'#2ecc71'"> // bind the color hex code to style.background. becareful, since the content in side "" is expression. so '' is needed to wrap the string.

<div [ngStyle]="{background: checkedIn? '#2ecc71': 'ffffff'}"> // use ngStyle to add multiple styles to it.

Pipes for data transformation

pipes can be used to transform the data right before rendering.

{{dateInSeconds | date: 'yMMM'}}  {{ name | json}}

There are bunch of built in pipes available here. And you can create custom pipes to transform the data for rendering

Safe navigation

Use ? operator to avoid null pointer exception.

children?.length // '?' is used as safe navigator. If children is null or undefined, the whole expression is finished, otherwise access 'length' member of children. 

let children? : string[];
length = children?.length || 0; // return children.length if children exists, otherwise return 0.

Use ?? for null checking and return default value

const yourName = name??'default_name' ;
// is equal to 
const yourName = name == null? 'default_name': name; 

Component architecture and feature modules

Dumb and smart component

Dumb/presentational component just renders the UI via @input, and emit events via @output

Smart component communicates with services, and render child components.

One way data flow

The event emits up, the data flows down.

<div [data]="companies" (change)="handleChange()">

Ng-template with context

<ng-container *ngTemplateOutlet="child;context:ctx> // ctx is an context object
<ng-template #child let-node></ng-template> // node = object assigned by $implicit

.ts file
ctx = {
 $implicit = object;
 // other keys 

Immutable state changes

remove elements from the array

names = names.filter(name => !== id); // remove names whose id is id

edit elements in an array.

names = => {
  if ( === {
    name = Object.assign({}, name, event) // the name object is merged with the event object. when conflict property value happens,  the property value in event object prevails.
  return name;

Service, HTTP and Observation

// service file
export class PassengerService {
  getPassengers(): Passengers[] {
    return [];

// module file
import: [],
export: [],
declaration: [],
providers: [PassengerService] // make the service available in the current module. Make it available for injection.

// component file
// dependency injection
constructor(private passengerService: PassengerService) {}

Injectable annotation

// PersonService.ts
import {HttpClient} from '@angular/common/http';
import {Injectable} from '@angular/core';

@Injectable() // This means this service could use inject dependency (HttpClient)
export class PersonService {
  constructor(private http: HttpClient){}

// module.ts
import {HttpClientModule} from '@angular/common/http';

  imports:      [ BrowserModule, FormsModule, HttpClientModule ],

Http data fetch with Observerables

Http put, delete with immutable state

Headers and requestOptions

// PersonService.ts
const PASSENGER_API: string = '/api/passengers';
@Injectable() // This means this service could use inject dependency (HttpClient)
export class PersonService {
  constructor(private http: HttpClient){}

  getPassengers(): Observable<Passenger[]> {
    return this.http.get(PASSENGER_API)
        .map((response: Response) => {
           return response.json();

  updatePassenger(passenger: Passenger): Observable<Passenger> {
    let headers = new Headers({
      'Content-type': 'application/json'
    let options = new RequestOptions({headers: headers});
    return this.http.put(`${PASSENGER_API}\${}`, passenger, options)
           .map((response: Response) => {
             return response.json();

  removePassenger(passenger: Passenger): Observable<Passenger> {
    return this.http.delete(`${PASSENGER_API}\${}`)
           .map((response: Response) => {
             return response.json();

// component.ts
constructor(private passengerService: PassengerService){}
ngOnInit() {
  this.passengerService.getPassengers().subscribe((data: Passenger[]) => {
    this.passengers = data;

handleEdit(event: Passenger) {
  this.passengerService.edit(event).subscribe((data: Passenger) => {
    this.passengers = Passenger) => {
    if( === {
      passenger = Object.assign({}, passenger, event);
  return passenger;

handleRemove(event: Passenger) {
  this.passengerService.remove(event).subscribe((data: Passenger) => 
  { // data is not used here, just as a placeholder.
  this.passenger = this.passenger.filter((passener: Passenger) => {
    return !==;

Http promises alternative

toPromise() operator could map the observable to promise.

Observable throw error handling

// service.ts

return this.http.get(PASSENGER_API).map((response : Response) => response.json()).catch((error: any) => Observable.throw(error.json()));

// caller.ts
this.passengerService.getPassenger().subscribe((data: Passenger[]) => handle_data, (error: Error) => handle_error);

Angular Template Driven Form

set up form container component

set up form stateless component: container passes input value to stateless component.

ngForm and ngModel, radio button, checkBox

// stateless component
<from #form="ngForm"> // activate ngForm directive, assign the model to template reference variable 'form'
  // input box
  <input ngModel type="number" name="id"> // ngModel: bind the input to the form object, with a key named 'id' 
  // radio button
  <label><input ngModel (ngModelChange)="toggleCheckIn($event)" name="isCheckedIn" type="radio" [value]="true">Yes</label>
  <label><input ngModel (ngModelChange)="toggleCheckIn($event)" name="isCheckedIn" type="radio" [value]="false">No</label>
  // checkbox
  <label><input ngModel (ngModelChange)="toggleCheckIn($event)" name="isCheckedIn" type="checkbox">Check in</label>
  // options 
  <select name="baggage" [ngModel]="detail?.baggage">
    <option *ngFor="let item of baggage" 
    [selected]="item.key === detail?.baggage" // pre-select an option based on the input. 
  // You can also use [ngValue]="item.key" to replace [value] and [selected]

{{ form.value | json }} // {'id': xxx}
export class PassengerFormComponent {
  detail: Passenger;

  toggleCheckIn(checkedIn: boolean) {
     detail.checkedInDate =;   

Form validation

<form #form="ngForm">
  <input #fullname="ngModel" required>
  <div *ngIf="fullname.errors?.required && fullname.dirty"> Fullname is required</div>
  {{ fullname.errors | json}} // {"required": true}
{{form.valid | json}} // boolean
{{form.invalid | json}} // boolean

Form submit

update: EventEmitter<Passenger> = new EventEmitter<Passenger>();

<form (ngSubmit)="handleSubmit(form.value, form.valid)" #form="ngForm">

  <button type="submit" [disabled]="form.invalid"></button>

handleSubmit(passenger: Passenger, isValid: boolean) {
  if (isvalid) {

Component routing

Base href

  <base href="/"> // Important to include it in the index.html

// module.ts
import {RouterModule} from '@angular/router'

404 handling, routerLink, routerLinkActive


const routes: Routes = [
  { path: '', component: HomeComponent, pathMatch: 'full'},
  { path: '*', component: NotFountComponent}

  declarations: [],
  imports: [],
  bootstrap: [AppComponent]

// NotFoundComponent.ts
  selector: 'not-found'
  template: `<div> Not Found</div>`

// app.component.ts

template: `
  <div class='app'>
    <a routerLink='/' routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">Home</a> // routerLinkActive: add 'active' class to the link if it's active.

// .scss

a {
  &.active {
    color: #b690f1;

For multiple elements in nav, you can also use an ngFor to initialize the <a> links.

In a child module, use RouterModule.forChild() to register the router module for child.

Children route, Router params

const route: Routes = [
  path: 'passengers',
  children: [
    {path: '', component: PassengerDashboardComponent}, // maps to '/passengers'
    {path: ':id', component: PassengerViewComponent}, // maps to '/passengers/123423' any number

Route and ActivatedRoute

template: `
  <button (click)="goBack()">Go Back</button>

constructor(private router: Router, private route: ActivatedRoute)

  this.route.params.switchMap((data: Params) => this.passengerService.getPassenger( Passenger) => { = passenger;})
// switchMap - listen to an observerable, and switch to return to another observable. The previous observable is cancelled once the value is returned. 

viewPassenger(event: Passenger){
  this.router.navigate(['/passengers',]); // /passengers/:id

Hash location strategy: the parts after ‘#’ symbol in the url never sends to the server.

Thus the parts after the ‘#’ symbol could be used to record the client state. like anchor in the page, etc.

Redirect to

const routes: Routes = [
  {path: '', redirectTo: '/passengers'}
] // the '' page would be redirected to the '/passengers' page

Angular Pro Course

Content Projection with <ng-content>

the content in the selector tag could be projected to the child component html using <ng-content>.

<auth-form (submitted)="login($event)")>
  <button type="submit">login</button>
<auth-form (submitted="signUp($event)")>
  <h3>Sign Up</h3>
  <button type="submit">join us</button>

// auth-form.ts

template: `
  <ng-content select="h3"></ng-content>
   <ng-content select="button"></ng-content> // select is a query selector, it can also select a class by using '.class-name'

Content projection can also bind a component element, rather than basic HTML elements.

Measure the Web App performance

The Web Apps should aim for refreshing the page at 60fps. That is, finish a rendering cycle within 16ms. The javascript should be finish within 10ms, thus the browser has 6ms to do the housekeeping works.

The full pixel pipeline


Javascript: 运行javascript,包括更新变量值,更新DOM,更新variable和DOM element里的binding。

Style:计算每个element应该attach to哪个css class





The full pixel pipeline
case 1. 所有阶段都被执行一遍
The  pixel pipeline without layout.
case 2. layout不用执行。比如element的layout不用改。可能只改了UI string,换了背景颜色等。
The pixel pipeline without layout or paint.
case 3. layout和paint都不用改,也即画面不需要update。这种cycle最节省资源。

Udacity的课程,如何优化Web App的performance–ud860

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.