On Github davinkevin / Angular2-Presentation
Since NG-Europe 2014 and the announcement of Angular2 , a lot of change have been made...
The core is still "Angular"
Keep only the good parts of the original Framework
A lot of people was discourage by the change
The AngularTeam work hard to improve Migration Path
An extension of Javascript should have been the reference language
But Google/Microsoft teams-up to improve TypeScript
It's True but the AngularTeam have something to help the migration...
No, It will be supported during two years after the release of Angular2
And your app won't stop working after this...
AngularJS is a macro-framework with :
Help to conserve SOLID principle in application
angular.module('myApp', []) .constant('Engine', function(){}). .constant('Tires', function(){}). .service('Car', Car); function Car(Engine, Tires){...}; Car.$inject('Engine', 'Tires');
In ES2015
import { Inject } from 'angular2/core'; class Car { constructor(@Inject(Engine) engine, @Inject(Tires) tires) {} }
In TypeScript
class Car { constructor(engine: Engine, tires: Tires) {} }
So it's just a Map<Token, Provider>
import { Injector } from 'angular2/core'; ... let injector = Injector.resolveAndCreate([ Car, Engine, Tires ]); let car = injector.get(Car);
import { Injector, provide } from 'angular2/core'; ... let injector = Injector.resolveAndCreate([ provide(Car, {useClass: Car}), provide(Engine, {useClass: Engine}), provide(Tires, {useClass: Tires}) ]);
import { Injector, provide } from 'angular2/core'; ... let injector = Injector.resolveAndCreate([ Car, // Class as a Provider provide(Car, {useClass: Car}), // Full description of Provider, allow aliases provide(Car, { useFactory : (engine, tire) => new Car(engine, tire), deps : ['Engine', 'Tire'] }), // Provide Car from Factory provide(Tire, {useValue: "Tire"}), // Use Value SuperEngine, provide(Engine, {useExisting : SuperEngine }), // Use Alias ]);
Singleton only inside the same injectors
import { Injector } from 'angular2/core'; let injector = Injector.resolveAndCreate([Engine]); let childInjector = injector.resolveAndCreateChild([Engine]); injector.get(Engine) === injector.get(Engine); injector.get(Engine) !== childInjector.get(Engine);
Zones are simply an Execution context for Javascript
Zones are simply an Execution context for Javascript
It helps to handle asynchronous code in JS
Extract from Dart Lang, alternative to Domains in NodeJS
function cf(name) { return function() { console.log(name); } }; var a = cf('a'), b = cf('b'), c = cf('c'), d = cf('d'); a(); setTimeout(b, 0); setTimeout(c, 0); d();
Results to
a d b c
All the async tasks are added to the event queue and executed when other tasks are finished
start(); a(); setTimeout(b, 0); setTimeout(c, 0); d(); stop();
Results :
// Start a d // Stop b // Missed c // Missed
Let's consider a framework, for example AngularJS with this:
var $rootScope; zone .fork({ afterTask : function() { $rootScope.$digest(); } }) .run(function() { angular.bootstrap(document, aModule); $rootScope = angular .element(document) .injector() .get('$rootScope'); });
$scope.$watch('foo.bar', (newVal, oldVal) => { ... });
$scope.$watch('foo.bar', (newVal, oldVal) => { if (newVal.length <= 0) return; let firstTransformation = removeSpaces(newVal); let secondTransoformation = firstTransformation.toUpperCase(); $scope.result = thirdTransformation; });
We can write our previous $watch like this...
foo.bar$ // An Observable .filter(v => v.length > 0) .map(v => removeSpaces(v)) .map(v => v.toUpperCase()) .subscribe( v => $scope.result = v /* An Observer */ );
Yes, and it's very fun !
Observable are immutable, composable and reusable
let obs$ = Rx.Observable.from(1, 2, 3, 4, 5); obs.subscribe(x => console.log(x)); // 1, 2, 3, 4, 5 let evenObs$ = obs.filter(x => x%2 == 0 )); evenObs$.subscribe(x => console.log(x)) // 2, 4 evenObs$.map(x => x*2).subscribe(x => console.log(x)) // 4, 8
A polifyll for ES2017 (?) module loader
In AngularJS (it's so 2009...)
angular.module('FooModule', [ 'BarModule', 'anotherModule' ]);
In ES2015 & SystemJS
import { BarModule } from './Bar.comp'; import { AnotherComp } from './another.comp';
Simple to maintain !
Angular2 is fully modular
Reduce the size of the app depending of what your app need
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory() : typeof define === 'function' && define.amd ? define(factory) : (factory()); }(this, function () { 'use strict'; function myAwesomModule() { ... } }));
No... you can use what you want
Born in 2006 and help a lot of Web developers...
Good fallback to do more in a complicated world
let firstClass = document.querySelector('.some-class'); let allOfClass = document.querySelectorAll('.some-class');
let elem = document.querySelector('#some-element'); elem.classList.add('some-class'); // Add class elem.classList.remove('some-other-class'); // Remove class elem.classList.toggle('some-other-class') // ...toggle... elem.classList.contains('some-third-class') // Does it have some class ?
Same thing on inline style...
elem.style.color; // Get a CSS attribute elem.style.color = 'rebeccapurple'; // Set a CSS attribute elem.style.minHeight; // Get a CSS attribute elem.style.minHeight = '200px'; // Set a CSS attribute
let elem = document.querySelector('#some-element'); elem.addEventListener('click', () => console.log('clicked'), false); elem.addEventListener('customEvent', anotherFunction, false);
let elem = document.querySelector('#some-element'); let parent = elem.parentNode; let childs = elem.childNodes; let specificChild = elem.querySelector('anotherClass'); let specificChilds = elem.querySelectorAll('anotherClass');
var TodoComp = ng .Component({ selector: 'todo', templateUrl: '../partials/todo.html' }) .Class({ constructor: function (heroService, routeParams) {} select(todo){...} });
But, it isn't as fun as evolved JS
@Component({ selector: 'todo', templateUrl: '../partials/todo.html' }) export class TodoComp { constructor( private heroService: HeroService, private routeParams: RouteParams ){} select(todo : Todo) : Boolean {...} }
All is about Component
EVERYWHERE !
Template + Controller (+ optional RouteConfig)
<div>My title is {{ title }}</div>
No more ng-x directives
Instead, we use standard HTML for property binding
<h3 ng-hide="vm.isHidden" ng-class="{active : favoriteHero === hero }" > Your favorite hero is: {{ vm.favoriteHero }} </h3>
becomes...
<h3 [hidden]="isHidden" [class.active]="favoriteHero === hero" > Your favorite hero is: {{ favoriteHero }} </h3>
Really no more ng-x directives
Again, we use standard HTML for event binding
<input ng-click="vm.send(foo)">Click me !</input>
becomes...
<input (click)="send($event)">Click me !</input>
It disappears, so we have to do it manually
<input [value]="hero.firstName" (input)="hero.firstName=$event.target.value" >
The API evolves to introduce a syntax for the two-way binding in latest Alpha
<input [(ngModel)]="hero.firstname">
This syntax is a mix between property and event bindings
All the directive inducing a dom modification outside of the current element are called Structural Directive
<p *ngIf="condition"> condition is true and ngIf is true. </p>
<tr *ngFor="let movie of movies">{{movie.name}}</tr>
But Why ?
<template> <div>A super template !</div> </template>
This template isn't rendered by the browser but can be used
<p *ngIf="condition"> A super template ! </p>
<template [ngIf]="condition"> <p> A super template ! </p> </template>
So, the * is just a syntaxic sugar
And we can write our own Structural Directive !
We can define a template variable associated to a dom element
<video #player src="foo.mp4"></video> <button (click)="player.play()"></button>
Still holding all the logic of the component
And compared to Angular1, a component is a tiny "App"
import { Component } from 'angular2/core'; @Component({ selector: 'todo', template: `<h1>{{ title }}</h1>` }) export class TodoComp { public title : String = 'A super presentation !!'; }
import { Component } from 'angular2/core'; import { MyService } from './myservice'; @Component({ selector: 'todo', template: `<h1>{{ title }}</h1>` }) export class TodoComp { public title : String = 'A super presentation !!'; constructor(private _myService : MyService){} // DI in constructor }
import { Component } from 'angular2/core'; import { MyService } from './myservice'; @Component({ selector: 'todo', template: `<h1>{{ title }}</h1>`, providers : [ MyService ] // Do you remember the injector part ? }) export class TodoComp { public title : String = 'A super presentation !!'; constructor(private _myService : MyService){} // DI in constructor }
import { Component } from 'angular2/core'; import { MyService } from './myservice'; @Component({ selector: 'todo', template: ` <h1>{{ title }}</h1> <emitter></emitter> `, providers : [ MyService ] }) export class TodoComp { public title : String = 'A super presentation !!'; constructor(private _myService : MyService){} }
import { Component } from 'angular2/core'; import { MyService } from './myservice'; import { EmitterComponent } from "./emitter.component"; @Component({ selector: 'todo', template: `<h1>{{ title }}</h1> <emitter></emitter>`, directives : [ EmitterComponent ] // Inclusion of all subComp needed providers : [ MyService ] }) export class TodoComp { public title : String = 'A super presentation !!'; constructor(private _myService : MyService){} }
import { Component } from 'angular2/core'; import { MyService } from './myservice'; import { EmitterComponent } from "./emitter.component"; @Component({ selector: 'todo', template: `<h1>{{ title }}</h1> <emitter></emitter>`, directives : [ EmitterComponent ] // Inclusion of all subComp needed providers : [ MyService ] }) export class TodoComp implements OnInit { public title : String = 'A super presentation !!'; constructor(private _myService : MyService){} ngOnInit() { console.log('The Component is initialised'); } }
No more headache with [pre|post]link and compile
We have now a component, but we want to communicate with other element
How could we do that without any DDO ?
import {Component, Input} from 'angular2/core'; @Component({ selector: 'counter' template: `...` }) export class CounterCmp { @Input() counterValue = 0; }
<counter [counterValue]="10"></counter>
import {Component, Input} from 'angular2/core'; @Component({ selector: 'counter' template: `...` }) export class CounterCmp { @Input('start') counterValue = 0; }
<counter [start]="10"></counter>
import {Component, Input} from 'angular2/core'; @Component({ selector: 'counter' template: `...` }) export class CounterCmp { @Input('start') counterValue = 0; @Output() stateEmitter = new EventEmitter<State>(); }
<counter [start]="10" (stateEmitter)="show($event)"></counter>
import {Component, Input} from 'angular2/core'; @Component({ selector: 'counter' template: `...` }) export class CounterCmp { @Input('start') counterValue = 0; @Output('finish') stateEmitter = new EventEmitter<State>(); myState : State = { numberOfStop : 0, startDate : Date.now(), finishDate : null }; finish() { this.stateEmitter.emit(myState); } }
<counter [start]="10" (finish)="show($event)"></counter>
No more of
<foo bar="val"></foo> <foo bar="{{ val }}"></foo> <foo bar="val()"></foo> <foo bar="val"></foo> <!-- the word 'val', not the value'
It's where the logic of your app should be...
We can declare a service like this
export class MyService{}
providers : [ MyService ]
Just need one decorator to allow DI
import {Injectable} from 'angular2/core'; @Injectable() export class MyService{ constructor(private _http : http){} }
We don't have time to deep dive on everyone, but we can mention it
A `component` logic lead to a Component Router
The UI-Router alternative was quite popular
We can encapsulate everything, and we can do it by multiple way
import {ViewEncapsulation} from 'angular2/core'; @Component({ selector: 'comp', templateUrl: '<div class="my-comp">An encapsulated comp</div>', styles: [`.my-comp { background: green; }`], encapsulation: ViewEncapsulation.Emulated }) class Comp {}
Which is the default encapsulation system
<head> <style>.my-comp[_ngcontent-1] {background: green;}</style> </head>
<comp _ngcontent-0 _nghost-1> <div class="my-comp" _ngcontent-1> An encapsulated comp </div> </comp>
Which uses the Shadow-DOM
encapsulation: ViewEncapsulation.Native
<comp> #shadow-root | <style> | .my-comp { background: green; } | </style> | <div class="my-comp">An encapsulated comp</div> </comp>
Take care of Side effect !
encapsulation: ViewEncapsulation.None
<head> <style>.my-comp {background: green;}</style> </head>
<comp _nghost-1> <div class="my-comp"> An encapsulated comp </div> </comp>
Derived from ember-cli
$ ng new project $ ng generate component my-new-component $ ng build $ ng test $ ng e2e $ ng server
The JS will live out the window element
The progressive Web-App era
A way to do 'heavy' calculation in another thread
After Mobile First, Offline-First
Cache
Offline
Synchronisation
Also know as Server-Side-Rendering
import {bootstrap} from 'angular2/platform/browser'; import {AppComponent} from './app.component'; bootstrap(AppComponent);
The Angular Team works very hard around a way to transform our AngularJS application in Angular2 App
A module allowing to Upgrade and Downgradeelements of Ng1 and Ng2
import {UpgradeAdapter} from 'angular2/upgrade'; var adapter = new UpgradeAdapter(); var app = angular.module('myApp', []); adapter.bootstrap(document.body, ['myApp']);
app.directive('productDetail', adapter.downgradeNg2Component(ProductDetail));
@Component({ selector: 'product-list', directives: [ adapter.upgradeNg1Component('productListItem') ], template: `<h2>Product List</h2> <product-list-item [product]="product"></product-list-item>` }) class ProductList { @Input() products: Product[]; ... }
The most important requirement to do the migration is...
Good coding style in Angular 1.5+ !
If we have the time...
Any questions ?
http://goo.gl/j1UmGA