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
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: [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
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
@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
Nested routes
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>
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
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