Angular with TypeScript

with Andrei Antal

slides.com/andreiantal/angular2_voxxed-25/

slides:

INTRO

About the speaker

Andrei Antal

frontend engineer @ Qualitance

 

  • JavaScript technologies enthusiast
  • Community activist
  • Occasional speaker
  • member of ngCommunity

Contact me at:

antal.a.andrei@gmail.com

ngBucharest

ABOUT

ANGULAR (2, 4, 5...)

But first a bit of history

2009 - Angular project - Misko Hevery, Adam Abrons

2012 - Angular 1.0 release

  • 2-way data binding
  • Custom elements (directives)
  • Dependency Injection

2013 - React from Facebook

  • Virtual DOM
  • JSX (HTML + JavaScript)
  • Unidirectional data flow

2010 - implemented @Google by Misko Hevery​

Announcing Angular 2

2014 - Angular 2 - first announcement

2014 - VueJS - Evan You

2015 - Angular 1.4

2016 - Angular 1.5 - 2 february (components)

2016 - Angular 2 - final release - 14 september

2017 - Angular v4 (just Angular) - 23 March

But first a bit of history

2017 - Angular v5 - 1 November

2016 - Angular 1.6 - 12 december

What's new in Angular

What's new in Angular 2

Features:

  • Simplified - modular structure
  • Component based architecture (already from Angular 1.5)
  • Blazing fast - no more digest cycle*
  • New compiler
  • New change detection mechanism
    • introducing zones
    • models as observables
  • New module system - ngModule
  • New Router
    • easier to implement lazy loading
  • Dropped bower in favor of npm for package management

What's new in Angular

Features:

  • New DI mechanism
  • Improved support for server side rendering - AOT compilation
  • New Observable based HTTP module
  • Supporting modern web standards (shadow DOM)
  • New (awesome) animations system

All platforms

All platforms

Community

ES6 AND TYPESCRIPT

JavaScript - ES5

Common problems with Javascript today

  • Dynamic typing
  • Lack of modularity
  • Verbose patterns to achieve some code sanity

ES6/ES2015

  • Classes
  • Arrow Functions
  • Template Strings Inheritance
  • Constants and Block Scoped Variables ...spread and ...rest
  • Destructuring
  • Modules

Classes

class Pizza {
    constructor(toppings) {
        this.toppings = toppings
    }

    listToppings() {
        toppings.forEach(function(topping) {
            console.log(topping.toString();
        }
    }
}

var pizza = new Pizza();
pizza.listToppings();

Arrow functions

// ES5 function

const sum = function(num1, num2) {
    return num1 + num2;
};

// ES6 arrow function

const sum = (num1, num2) => { return num1 + num2 };
// ES5 function
const getTempItem = function(id) {
    return {
        id: id,
        name: "Temp"
    };
};

// ES6 arrow function
const getTempItem = id => ({ id: id, name: "Temp" });
  • does not bind its own this
  • no arguments, super or new.target

Template strings

var firstName = "Andrei",
    lastName = "Antal";

// ES5

var fullName = "The full name is: " + firstName + " " + lastName


// ES6 template strings

var fullName = `The full name is: ${firstName} ${lastName}`;

Const and let

// ES5

function() {
  if(true) {
    var x = 1;
  }
  console.log(x) // 1
}

// ES6

function() {
  if(true) {
    let x = 1;
  }
  console.log(x) // ReferenceError: x doesn't exist here
}
function() {
  const a; // error, need to initialize const
  const b = 1;
  b = 2; // error cannot reasign
}

Destructuring

const animal = {
  name: 'Dog',
  sound: 'wof'
};

function makeSound(options) {
  options.name = options.name || 'animal';
  console.log(`The ${options.animal} goes ${options.sound}`)
}
function makeSound({name = 'animal', sound}) {
  console.log(`The ${name} goes ${sound}`)
}

makeSound(animal);
let options = {
  repeat: true,
  save: false
};

let {repeat, save} = options;

console.log(repeat); // true
console.log(save); // false

Others​

  • Rest and spread
  • Modules
  • Enhanced Objects
  • Iterators
  • Generators
  • Modules
  • Proxies

TypeScript

Static typing

let isDone: boolean = false;
let height: number = 6;
let name: string = "bob";
let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3];
enum Color {Red, Green, Blue};
let c: Color = Color.Green;
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean

Classes ++

class Person {
    name: string;
    nickName?: string;
    
    constructor(name: string) {
        ...
    }
}

const p = new Person("Andrei");

Interfaces

interface ClockInterface {
    currentTime: Date;
    setTime(d: Date);
}

class Clock implements ClockInterface {
    currentTime: Date;
    setTime(d: Date) {
        this.currentTime = d;
    }
    constructor(h: number, m: number) { }
}

Generics

//declare
private _possessions: Array<Thing>;

constructor(){
    //assign
    this._possessions = new Array<Thing>();
}

Decorators

@Component({
    selector: 'my-app',
    styles: ['h1 {color:#545454;}'] 
    template: `
    <h1>
        Hello from the {{componentName}}.
    </h1>
    `
})
export class AppComponent {
    @Input() componentName = 'AppComponent';
}

.

  • Functions that are invoked with a prefixed @ symbol, and immediately followed by a class, parameter, method or property.

BOOTSRAP AND COMPILATION

Bootstrap

  • No more ng-app directive for boostrap

 

  • Bootstarp is done explicitly

 

  • There is one Root Module which has one Root Component

 

  • Everything else is a child of the two

 

  • Just In Time (JIT) vs Ahead Of Time (AOT)

Bootstrap


import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {AppModule} from './app';


platformBrowserDynamic().bootstrapModule(AppModule)

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent }  from './app.component';

@NgModule({
  imports:      [ BrowserModule ],
  declarations: [ AppComponent ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }
  • Just In Time (JIT) compilation and bootstrap

Bootstrap

// The browser platform without a compiler
import { platformBrowser } from '@angular/platform-browser';

// The app module factory produced by the static offline compiler
import { AppModuleNgFactory } from './app/app.module.ngfactory';

// Launch with the app module factory.
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);
  • Ahead Of Time (JIT) compilation and bootstrap

index.html

....

<body>
    
    <my-app><!-- content managed by Angular --></my-app>

....

</body>

....

MODULES

MODULES

  • Class with @NgModel decorator
    • tells Angular how to compile and run module code
  • Built-in modules
    • ex: FormsModule, HttpModule, and RouterModule
  • Types
    • root module
    • feature modules
  • Different from  ES6 Modules - Angular spciffic

MODULES

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';

import { FeatureModule } from '../feature/feature.module';

import { SomeComponent } from './some.component';
import { SomeDirective } from './some.directive';
import { SomePipe } from './some.pipe';

import { SomeService } from './some.service';

@NgModule({
  declarations: [SomeComponent, SomeDirective, SomePipe],
  imports: [CommonModule, HttpClientModule, FeatureModule]
  exports: [SomeComponent],
  providers: [ SomeService ],
})
export class SomeModule {}

COMPONENTS

Component architecture

Component architecture

Component

  • @Component annotation
  • Communication
    • Inputs, @Inputs
    • Outputs, @Outputs
  • Component lifecycle hooks
  • Host element interactions

Component

import { Component } from '@angular/core';
import { SomeService } from './some.service';

@Component({
  selector: 'app-component',
  template: `
    <div class="component">
        <span> Hello, {{name}} </span>
        <button (click)="handleClick()"> Start </button>
    </div>
  `,
  styles: ['span { color: red }'],
})
export class AppComponent {
  @Input() name: string;
  @Output() onClick: EventEmitter;
  private views:number = 0;
  
  constructor(private someService: SomeService) { }
  
  onClick() { 
    this.views += 1; 
    this.onClick.emit(this.views); 
  }
}

Component

import { Component, OnInit } from '@angular/core';
import { SomeService } from './someService';

@Component({
  selector: 'parent-component',
  template: `
    <div class="parent">
        <app-component
          [name]="guestName"
          (onClick)="handleClick($event)" 
        />
    </div>
    <span class="component">some text</span>
  `,
  styles: ['span { color: blue }'],
})
export class ParentComponent implements OnInit{
  private guestName:string;
  
  constructor() { }

  ngOnInit() { 
    this.guestName = "Andrei Antal";
  }
}

Component


import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent }  from './app.component';
import { ParentComponent }  from './parent.component';
import { SomeService }  from './some.service';

@NgModule({
  imports: [ BrowserModule ],
  declarations: [ 
    ParentComponent, 
    AppComponent
  ],
  exports: [ ParentComponent ],
  providers: [ SomeService ]
})
export class MyModule { }

Lifecycle Hooks

import {Component, OnChanges, OnInit, OnDestroy} from 'angular2/core';

@Component()
export class AppComponent implements OnChanges, OnInit, OnDestroy { 
  /* 1 */ constructor() { }

  // called when an input or output binding changes
  /* 2 */ ngOnChanges(changes) { }   
  
  // after child initialisation
  /* 3 */ ngOnInit() { } 
  
  // just before is destroyed
  /* 4 */ ngOnDestroy() { } 
}

Unidirectional data flow

Component types

TEMPLATING

Templates

import { Component } from '@angular/core';
import { SomeService } from './someService';

@Component({
  selector: 'my-component',
  template: `
    <div>
        Hello component
    </div>
  `,
  styles: [`div { color: red }`],
})
export class MyComponent {
  @Input() name;
  @Output() onClick: EventEmitter;
  
  constructor(private someService: SomeService) {
  }

  ...
}

Templates


<rate-button 
    [talk]="myTalk" 
    (rate)="handleRate($event)"
></rate-button>
  • Property and event bindings

<input [(ngModel)]="todo.text"></input>
  • Two way bindings

<input [ngModel]="todo.text" (ngModelChange)="todo.text=$event">

It's just syntactic sugar for

Templates


<div>Rating {{rating}}</div>
  • Interpolation
<talk-cmp 
    *ngFor="let t of talks; let i=index" 
    [talk]="t" 
    [index]="i"
></talk-cmp>
  • References

<div [textContent]="interpolate(['Rating'], [rating])"></div>

It's just syntactic sugar for


<confirmation-dialog #dialog></confirmation-dialog>
<button (click)="dialog.open()">Open</button>
  • Directives

Template syntax

Syntax Binding type
<h1>{{title}}</h1> Interpolation
<img [alt]="Some image"> Property
<li [class.active]="isActive"></li> Class
<div [style.width.px]="mySize"> Style
<button (click)="onClick($event)"> Event
<input [(ngModel)]="data.value"> Two-way

    DEPENDENCY INJECTION

    Dependency Injection

    Features

    • Framework for resolving dependencies
    • Automatic creation and resolution of resources
    • The injector - created at bootstrap; configure it by adding providers
    • Register providers
      • in modules - each service is registered in the root injector => available in the entire application
      • in components - a special instance to be used in that component and all children

    Services - Injectables

    import { Injectable } from '@angular/core';
    
    @Injectable()
    export class ExampleService {
        someMethod() {
            return 'Hey!';
        }
    }

    Creating providers​

    Dependencies

    import { FormsModule } from '@angular/forms';
    import { HttpClientModule } from '@angular/common/http';
    import { ExampleService } from './example.service';
    
    @NgModule({
      imports: [ BrowserModule, FormsModule, HttpClientModule ],
      declarations: [ App ],
      providers: [ ExampleService ],
      bootstrap: [ App ]
    })
    export class AppModule {}

    Registering providers​

    Dependencies

    import { Component } from '@angular/core';
    import { ExampleService } from './example.service';
    
    
    @Component({
        selector: 'my-app',
        template: '<h1>{{ title }}</h1>',
        providers: [ExampleService]
    })
    
    export class AppComponent { 
        title: string;
    
        constructor(private _exampleService: ExampleService) { }
    
        ngOnInit() { 
            this.title = this._exampleService.someMethod(); 
        }
    }

    Injecting dependencies

    HTTP

    HTTP Services

    Using Data services

    • Singleton instance
    • Share state between Components
    • Leverage Observables

    The HTTP Module

    • Primary protocol for client/server communications
    • Implements XMLHttpRequest (XHR) and JSONP
    • Http methods:  GET, POST, PUT, DELETE, PATCH and HEAD

    HTTP Client Module

    import { FormsModule } from '@angular/forms';
    import { HttpClientModule } from '@angular/common/http';
    import { ExampleService } from './example.service';
    
    @NgModule({
      imports: [ BrowserModule, FormsModule, HttpClientModule ],
      declarations: [ App ],
      providers: [ ExampleService ],
      bootstrap: [ App ]
    })
    export class AppModule {}

    Creating a HTTP Service

    // src/users.service.ts
    import { Injectable } from '@angular/core';
    import { HttpClientModule } from '@angular/common/http';
    
    @Injectable()
    export class UsersService {
      constructor(private http: HttpClientModule) { }
      get() {
        return this.http.get('/assets/users.json')
      }
    }

    Consuming a HTTP Service

    import { Component } from '@angular/core';
    import { UsersService } from './users.service';
    
    @Component({
      selector: 'users',
      template: `<h1>Users</h1>
        <tr *ngFor="let user of userslist">
          <td>{{user.username}}</td>
        </tr>`
    })
    export class Users {
      private userslist;
      
      constructor(private users: UsersService) { }
    
      ngOnInit() {
        users.get().subscribe( userResult => this.usersList = userResult );
      }
    }

    Consuming a HTTP Service

    import { Component } from '@angular/core';
    import { UsersService } from './users.service';
    
    @Component({
      selector: 'users',
      template: `<h1>Users</h1>
        <tr *ngFor="let user of usersList | async">
          <td>{{user.username}}</td>
        </tr>`
    })
    export class Users {
      private userslist;
      
      constructor(private users: UsersService) {}
    
      ngOnInit() {
        this.usersList = this.users.get();
      }
    }
    • Async pipe

    RxJS

    Streams

    Observables

    Observables vs Promises

    • Observables handle more than one value over time, so we need  complete function
    myObservable.subscribe(successFn, errorFn)
    
            myPromise.then(successFn, errorFn)
    myObservable.subscribe(successFn, errorFn, completeFn)
    
            myPromise.then(successFn, errorFn)
    • Why observables

    • Flexible: sync or async
    • Powerful operators
    • Less code to write

    ROUTER

    ROUTER

    • Main features
    • Url based navigation
    • Nested routes
    • Navigation guards
    • Resolvers
    • Easy lazy loading, preloading

    Configure routes

    import { Routes, RouterModule } from '@angular/router';
    import { Home } from './home.component';
    import { About } from './about.component';
    import { NotFound } from './notfound.component';
    
    const appRoutes: Routes = [
      { path: '', redirectTo: 'home', pathMatch: 'full' },
      { path: 'home', component: Home },
      { path: 'about', component: About },
      { path: '**', component: NotFound }, //always last
    ];
    
    export const AppRouting = RouterModule.forRoot(appRoutes, { useHash: true });

    Configure routes

    import { FormsModule } from '@angular/forms';
    import { HttpClientModule } from '@angular/common/http';
    import { ExampleService } from './example.service';
    import { AppRouting } deom './app.routing';
    
    @NgModule({
      imports: [ 
        BrowserModule,
        FormsModule,
        HttpClientModule,
        AppRouting
      ],
      declarations: [ App ],
      providers: [ ExampleService ],
      bootstrap: [ App ]
    })
    export class AppModule {}

    Lazy load routes

    import { Routes, RouterModule } from '@angular/router';
    import { Home } from './home.component';
    import { About } from './about.component';
    import { NotFound } from './notfound.component';
    
    const appRoutes: Routes = [
      { path: '', redirectTo: 'home', pathMatch: 'full' },
      { path: 'home', component: Home },
      { path: 'about', component: About },
      {
        path: 'details',
        loadChildren: 'app/details.module#DetailsModule',
      },
      { path: '**', component: NotFound }, //always last
    ];
    
    export const AppRouting = RouterModule.forRoot(appRoutes);

    Lazy load routes

    import { RouterModule } from '@angular/router';
    import { NgModule } from '@angular/core';
    import { CommonModule } from '@angular/common';
    
    import { DetailsComponent } from './details.component';
    
    @NgModule({
      imports: [
        CommonModule,
        RouterModule.forChild([{
          path: '',
          component: DetailsComponent,
        },
        {
          path: ':id',
          component: DetailsComponent,
        }]),
      ],
      declarations: [ DetailsComponent ],
      exports: [ RouterModule ]
    })
    export class DetailsModule { }

    FORMS

    FORMS

    Template driven forms

    • Two-way data binding
      • [(ngModel)]
    • Change tracking
      • ngForm
    • Standard validations

    Model driven / Reactive

    • Driven programmatically
      • FormBuilder, FormGroup, FormControl
    • Reactive APIs
      • eg - valueChanges
    • Composable Validations

    Form/Control states

    • States
      • touched, untouched
      • pristine, dirty
      • valid, invalid
    • Examples
      • f.form.valid, email.valid
    • CSS classes
      • ng-valid, ng-invalid

    Form example

    @Component({
      template: `
        <form #f="ngForm" (submit)="onSubmit(f.form.value)">
            <input type="email" 
               [(ngModel)]="model.email"
               name="email"
               #email="ngModel"
               required>
            <div [hidden]="email.valid">Email is required</div>
            <button type="submit" [disabled]="!f.form.valid">Submit</button>
        </form>`
    })
    export class Contact {
      model = {};
    
      onSubmit(value) {
        console.log(`Submitted: ${JSON.stringify(value)}`);
      }
    }

    ANGULAR CLI

    Angular CLI

    • The Angular CLI makes it easy to create an application that already works, right out of the box
    // installing
    $ npm install -g @angular/cli
    
    // scaffold the project
    $ ng new PROJECT_NAME
    $ cd PROJECT_NAME
    
    // start the project
    $ ng serve
    
    // generate components 
    $ ng g component my-new-component

    THANKS!

    bit.ly/2zqBxP7​

    Repo:

    goo.gl/zGbsf5

    Instructions:

    Angular with TypeScript Workshop

    Made with Slides.com