An introduction to Angular2

Preview:

Citation preview

Angular2

Michał PrzyszczypkowskiAngular2

By Michał Przyszczypkowski

Background

revolution instead of evolution

currently in BETA (since December 2015)

release date not yet announced

Angular2

By Michał Przyszczypkowski

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 { 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

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");

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

Decorators are proposed as standard for ES

Already implemented in TS

Annotations / Decorators

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

}

Decorators are proposed as standard for ES

Already implemented in TS

Annotations / Decorators

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

}

Decorators are proposed as standard for ES

Already implemented in TS

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

Modules

export interface Person { name: string;}

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

export const value:string = 'Something';

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

Angular2

App is made of components

Tree structure

Angular2

App is made of components

Tree structure

Concepts from AngularJS 1.x no longer

relevant

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.'); }}

Components

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

onClickMe(){ alert('Hello.'); }}

properties

methods

component

config

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>

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); }}

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"); }}

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 { ...}

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.'); }}

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>

Template language

<click-me message="Peekaboo"></click-me>

<click-me [message]="peekabooVariable"></click-me>

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"> {{ item.name }} </span>

Structural directives

<span *ngFor="#item of items; #index = index"> item no {{ index }} </span>

<span *ngFor="#item of items"> {{ item.name }} </span>

Structural directives

<span *ngFor="#item of items; #index = index"> item no {{ index }} </span>

<span *ngFor="#item of items"> {{ item.name }} </span>

explicit declaration

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>

Build-in directives

<span [class.blue]="isBlue"> TEXT </span>

<span [style.backgroundColor]="colorVariable"> TEXT </span>

Build-in directives

<span [class.blue]="isBlue"> TEXT </span>

<span [style.backgroundColor]="colorVariable"> TEXT </span>

<span [style.display]="isHidden ? 'none' : 'block'"> TEXT </span>

Build-in directives

<span [class.blue]="isBlue"> TEXT </span>

<span [style.backgroundColor]="colorVariable"> TEXT </span>

<span [hidden]="isHidden"> TEXT </span>

<span [style.display]="isHidden ? 'none' : 'block'"> TEXT </span>

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

<example-component> <h1>Inner title</h1> <span>Inner text</span></example-component>

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>

content will go

there

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

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

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

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 visibility

App

ItemsEdition ItemsList

C D

E

providers: [ItemsRepository]

Providers visibility

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

App

ItemsEdition ItemsList

C D

E

providers: [ItemsRepository]

providers: [ItemsRepository]

App

ItemsEdition ItemsList

C D

E

providers: [ItemsRepository]

providers: [ItemsRepository]

Each subtree has its own

instance of service.

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 { // ...}

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

Routing

routes point to components

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

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'}]}

Routing parameters

@RouteConfig([ {path: '/item/:id', component: Item, as: 'Item'}]}

constructor(params:RouteParams) { let routeParamValue:string = params.get('id');}

Nested routes

<router-outlet>

Nested routes

<router-outlet>

Nested routes

<router-outlet> <router-outlet>

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>

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 ItemsAddItem EditItem

Navigation

<a [routerLink]="['Home']">Home</a>

Navigation

<a [routerLink]="['Home']">Home</a>

let router:Router;router.navigate(['Home']);

Navigation

<a [routerLink]="['Home']">Home</a>

let router:Router;router.navigate(['Home']);

<a [routerLink]="['Items', 'Add']">Home</a>

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>

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?