Angular2

Michał Przyszczypkowski

Background

  • revolution instead of evolution
  • currently in BETA (since December 2015)
  • release date not yet announced

Typescript

  • Superset of JS (ES6)
  • Compiles to plain JS
  • Supported in all major IDE's
function greet(name: string):string {
    return "Hello, " + name;
}

let greeting: string = greet("Mike");

Strongly typed

class Student {
    public fullname : string;
    private age: number;
    private dontKnowWhatWillBeThere:any;

    constructor(public firstname:string, public lastname:string) {
        //...
    }
}

Classes & interfaces

class Student {
    lastname: string;
    fullname : string;

    constructor(firstname:string, lastname:string) {
        this.firstname = firstname;
        this.lastname = lastname;
    }
}
interface Person {
    firstname: string;
    lastname: string;
}

Classes & interfaces

function greeter(person : Person) {
    return "Hello, " + person.firstname + " " + person.lastname;
}

let user: Person = new Student("Mike", "Someone");
let user: Person = {firstname: 'Mike', lastname: 'Snow'}

Annotations / Decorators

@ExampleAnnotation({
  annotationKey: annotationValue
})
export class ExampleClass {

}
  • Decorators are proposed as standard for ES
  • Already implemented in TS

@AnotherExampleAnnotation({
  annotationKey: annotationValue
})
doSomething() {
    //...
}

Modules

import * as library from "./module";
import { Person, PeopleService } from "./module";

console.log(library.value);

let peopleSrv = new PeopleService();
let people: Person[] = peopleSrv.getPeople();
export interface Person {
    name: string;
}

export class PeopleService {
    getPeople(): People[] {
        return [{name: 'Mike'}];
    }
}

export const value:string = 'Something';

Angular2

  • App is made of components
  • Tree structure
  • Concepts from AngularJS 1.x no longer relevant
  • $scope, $directive, $controller, $service, $factory - no longer exist

Angular2

  • There is no $scope.$apply()
  • No need to use $timeout, $interval etc.
  • All events that may lead to bindings changes are patched within library
  • We don't need to handle changes detection anymore

Components

@Component({
  selector: 'click-me',
  templateUrl: 'template.html'
})
export class ClickMeComponent {
    private label: string = 'Click there!';

    onClickMe(){
        alert('Hello.');
    }
}
<body>
    <click-me></click-me>
</body>

properties

methods

component config

Selectors

@Component({
  selector: 'click-me'
  ...
})
<click-me></click-me>
@Component({
  selector: '[click-me]'
  ...
})
<div click-me=""></div>

Inputs

@Component({
  selector: 'click-me',
  templateUrl: 'template.html',
  inputs: ['message']
})
export class ClickMeComponent {
    private message: string;

    onClickMe(){
        alert(this.message);
    }
}
<click-me message="Peekaboo"></click-me>

Outputs

@Component({
  selector: 'click-me',
  templateUrl: 'template.html',
  outputs: ['onClicked']
})
export class ClickMeComponent {
    private onClicked: EventEmitter<string> = new EventEmitter<string>();

    onClickMe(){
        this.onClicked.emit("Hello");
    }
}
<body>
    <click-me (onClicked)="doSomething($event)"></click-me>
</body>

Styles

@Component({
  selector: 'click-me',
  templateUrl: 'template.html',
  styles: [`.click-btn {
      color: red;      
  }`]
})
export class ClickMeComponent {
    ...
}
@Component({
  selector: 'click-me',
  templateUrl: 'template.html',
  styles: [`.click-btn {
      color: red;      
  }`],
  encapsulation: ViewEncapsulation.None // Native / Emulated

})
export class ClickMeComponent {
    ...
}

Directives

@Directive({
  selector: '[click-me]',
  styles: [`.click-btn {
      color: red;      
  }`]
})
export class ClickMeDirective {
    ...
}

Template language

@Component({
  selector: 'click-me',
  templateUrl: 'template.html'
})
export class ClickMeComponent {
    private label: string = 'Click there!';

    onClickMe(){
        alert('Hello.');
    }
}
<button (click)="onClickMe()">{{ label }}</button>

Template language

<click-me message="Peekaboo"></click-me>
<click-me [message]="peekabooVariable"></click-me>
<click-me [message]="peekabooVariable" (onClicked)="doSth($event)"></click-me>

Structural directives

<span *ngFor="#item of items; #index = index"> item no {{ index }} </span>
<span *ngFor="#item of items"> {{ item.name }} </span>
<span *ngIf="isVisible"> conditional item </span>

explicit declaration

Build-in directives

<span [class.blue]="isBlue"> TEXT </span>
<span [style.backgroundColor]="colorVariable"> TEXT </span>
<span [hidden]="isHidden"> TEXT </span>
<span (click)="onClick()" (mouseenter)="onMouseEnter()"> TEXT </span>
<span [style.display]="isHidden ? 'none' : 'block'"> TEXT </span>

Transclusion

<div class="example-component-template">
    <h1>Outer title</h1>
    <ng-content></ng-content>
</div>
<example-component>
    <h1>Inner title</h1>
    <span>Inner text</span>
</example-component>
<div class="example-component-template">
    <h1>Outer title</h1>

    <h1>Inner title</h1>
    <span>Inner text</span>
</div>

content will go there

Services

class ItemsRepository {
    private items: Product[];
    
    getItems(): Products[] {
        return this.items;
    }
}
class ItemsRepository {
    private items: Product[];
    
    getItems(): Products[] {
        return this.items;
    }
}
import {ItemsRepository} from '../itemsRepo'

@Component({
  selector: 'click-me',
  templateUrl: 'template.html',
  providers: [ItemsRepository]
})
export class ItemList {
    private items: Product[];

    constructor(repo: ItemsRepository) {
        this.items = repo.getItems();
    }
}

Dependency Injection

first import

set as provider

inject in constructor

App

ItemsEdition

ItemsList

C

D

E

providers: [ItemsRepository]

Whole app share the same instance of ItemsRepository service

Providers visibility

App

ItemsEdition

ItemsList

C

D

E

providers: [ItemsRepository]

Each subtree has its own instance of service.

providers: [ItemsRepository]

class Api {
    loadItems(): Products[] {
        return this.items;
    }
}

DI between services

import {Api} from "./api";

@Injectable()
class ItemsRepository {
    constructor(private api:Api) { }
    
    getItems(): Products[] {
        this.api.loadItems();
    }
}

Mocking providers

import {FakeItemsRepository} from '../fakeItemsRepo'

@Component({
  selector: 'click-me',
  templateUrl: 'template.html',
  providers: [
    provide(ItemsRepository, {useClass: FakeItemsRepository})
  ]
})
export class ItemList {
    // ...
}

use custom provider

Routing

routes point to components

@RouteConfig([
    {path: '/', component: Home, as: 'Home'},
    {path: '/list', component: Items, as: 'List'}
]}
@Component({..})
class ...
<router-outlet></router-outlet>

Routing parameters

@RouteConfig([
    {path: '/item/:id', component: Item, as: 'Item'}
]}
constructor(params:RouteParams) {
    let routeParamValue:string = params.get('id');
}

Nested routes

<router-outlet>

<router-outlet>

<router-outlet>

Nested routes

@RouteConfig([
    {path: '/home', component: Home, as: 'Home'},
    {path: '/items/...', component: Items, as: 'List'}
]}
@Component({..})
class ...
<router-outlet></router-outlet>
@RouteConfig([
    {path: '/add', component: AddItem, as: 'Add'},
    {path: '/edit/:id', component: EditItem, as: 'Edit'}
]}
@Component({..})
class ...
<router-outlet></router-outlet>

Nested routes

/home

/items/add

/items/edit/1

Home

Items

Items

AddItem

EditItem

Navigation

<a [routerLink]="['Home']">Home</a>
let router:Router;
router.navigate(['Home']);
<a [routerLink]="['Items', 'Add']">Home</a>
<a [routerLink]="['Items', 'Edit', {id: 99}]">Home</a>
<a [routerLink]="['Item', {id:99}, 'Edit']">Home</a>

/item/:id/edit

Lifecycle hooks

@Component({...})
export class ComponentClass implements OnInit, OnDestroy {
    ngOnInit():any {
        ...
    }

    ngOnDestroy():any {
        ...
    }
}

How to hook?

Component lifecycle hooks

  • OnInit
  • OnDestroy
  • DoCheck
  • OnChanges
  • AfterContentInit
  • AfterContentChecked
  • AfterViewInit
  • AfterViewChecked

Router lifecycle hooks

  • CanActivate
  • OnActivate
  • CanDeactivate
  • OnDeactivate
  • OnReuse
  • https://angular.io/docs/ts/latest/guide/
  • http://blog.thoughtram.io/

Resources

Thank you.

Questions?

Angular2

By Michał Przyszczypkowski

Angular2

Introduction to Angular2 concepts

  • 213