Angular 2
@EmmanuelDemey
Emmanuel DEMEY
Consultant & Formateur Web
Zenika Nord
@EmmanuelDemey
Web / Domotique / Histoire / Biérologie
#kaiznday
Préparer votre application à Angular2
John Papa's styleguide
Autres Règles
- 1 composant AngularJS par fichier
- ECMAScript 2015
- Syntaxe Component As
- bindToController dans l'API des directives
- Component-First Approach
- Utilise le nouveau Router
- Création de service via la méthode service
Mise en pratique
eslint-plugin-angular
npm install --save-dev eslint eslint-plugin-angular
- Implémente le guideline de John Papa
- Rules :
- Component-First Approach : ng_no_controller
- controllerAs : ng_controller_as_*
Les choses que nous n'aimons pas...
Architecture AngularJS
DI (provider, service, factory...)
MV*
MV*
MV*
Filtres
API des Directives
app.directive('MyDirective', function(){
return {
restrict: 'AE',
require: '?^^ngModel',
scope: { variable: '@' },
compile: function(...) {
return {
pre: function(...) { ... },
post: function(...) { ... }
}
},
link: function(...) { ... }
}
});
API des Directives
app.directive('MyDirective', function(){
return {
restrict: 'AE',
require: '?^^ngModel',
scope: { variable: '@' },
compile: function(...) {
return {
pre: function(...) { ... },
post: function(...) { ... }
}
},
link: function(...) { ... }
}
});
API des Directives
app.directive('MyDirective', function(){
return {
restrict: 'AE',
require: '?^^ngModel',
scope: { variable: '@' },
compile: function(...) {
return {
pre: function(...) { ... },
post: function(...) { ... }
}
},
link: function(...) { ... }
}
});
API des Directives
app.directive('MyDirective', function(){
return {
restrict: 'AE',
require: '?^^ngModel',
scope: { variable: '@' },
compile: function(...) {
return {
pre: function(...) { ... },
post: function(...) { ... }
}
},
link: function(...) { ... }
}
});
<input ng-model="firstName">
<p>
{{firstName }}
</p>
2-way data-binding
2-way data-binding
<input ng-model="firstName"
ng-model-options="options">
<p>
{{firstName }}
</p>
2-way data-binding
<input ng-model="firstName">
<p>
{{::firstName }}
</p>
Et ce n'est pas fini...
Mais aussi...
- Pas de Server-Side Rendering
- Gestion des événements (ngClick, ...)
- Gestion des attributs HTML (ngSrc, ...)
La solution... Angular 2
Attention !
Version Alpha
P(eu|as) de Doc
- Architecture
- Composants
- Injection de Dépendance
- Pipes
Architecture Angular 2
<app></app>
menu
grid
gallery
DI
(classes ES6 ou TypeScript)
Pipes
(classes ES6 ou TypeScript)
La Famille JavaScript
ES5
ES2015
TypeScript
- Classe
- Modules
- ...
- Type Annotation
- Meta Annotation
- ...
Les Web Components
Custom Elements
Templates
Imports
Shadow DOM
Les composants
Composants Angular 2
- Ressources de base en Angular 2
- Tout est composant
- Application représentée par un arbre de composants
- Utilisation de métadonnées pour configurer un composant
//<my-app></my-app>
function MyAppComponent() {
}
MyAppComponent.annotations = [
new angular.ComponentAnnotation({
selector: 'my-app'
}),
new angular.ViewAnnotation({
template: "<main>" +
"<h1> This is my first Angular2 app</h1>" +
"</main>"
})
];
Composant version ES5
import {Component, View} from 'angular2/angular2';
@Component({selector: 'my-app'})
@View({
template: `<main>
<h1> This is my first Angular2 app</h1>
</main>`
})
class MyAppComponent {
}
Composant version TypeScript
import {Component, View, bootstrap} from 'angular2/angular2';
@Component({selector: 'my-app'})
@View({
template: `<main>
<h1> This is my first Angular2 app</h1>
</main>`
})
class MyAppComponent {
}
bootstrap(MyAppComponent);
Bootstrap de l'Application
Binding
Root Cpt
Child1 Cpt
Child2 Cpt
[property]="expression"
(event)="update()"
Property Binding
<input [attr]="expression" />
- Accès à toutes les propriétés des éléments HTML
- Possibilité de définir de nouvelles propriétés
- Compatibilité avec d'autres spécifications
Property Binding
<body>
<h1>My First Angular2 app</h1>
</body>
Property Binding
<body>
<h1 [textContent]="'My First Angular2 app'">
</h1>
</body>
Property Binding
<body>
<h1>{{'My First Angular2 app'}}
</h1>
</body>
Property Binding
//<beerItem [beer]="'Maredsous'"></beerItem>
@Component({
selector: 'beerItem',
inputs: ['beer']
})
@View({
template: `<section>
<h2>{{beer}}</h2>
<button>Je veux celle-ci !</button>
</section>`
})
class BeerItem {
beer: String;
}
Event Binding
<input (event)="expression" />
- Accès à tous les événements des éléments HTML
- Possibilité de définir de nouveaux événements
Event Bindings
//<beerItem [beer]="'Maredsous'" (selectBeer)="sendToBeerTap()"></beerItem>
@Component({
selector: 'beerItem',
inputs: ['beer'],
outputs: ['selectBeer']
})
@View({
template: `<section>
<h2>{{beer<}}</h2>
<button (click)="selectItem()">Je veux celle-ci !</button>
</section>`
})
class BeerItem {
beer: String;
selectBeer: EventEmitter;
selectItem() {
this.selectBeer.next(this.beer);
}
}
Attribute names must consist of one or more characters other than the space characters, U+0000 NULL, """, "'", ">", "/", "=", the control characters, and any characters that are not defined by Unicode.
Syntaxe valide ?
Syntaxe valide ?
Component Dependency
- Nécessité d'importer les composants nécessaires à votre application
- Propriété directives de @View
- Erreur si composants non utilisés
Component Dependency
import {Component, View, bootstrap, NgFor} from 'angular2/angular2';
import {BeerItem} from 'BeerItem';
@Component({ selector: 'my-app'})
@View({
template: `<main class="mdl-layout__content">
<ul class="googleapp-card-list">
<li *ng-for="#beer of beers">
<beerItem [beer]="beer"></beerItem>
</li>
</ul>
</main>`,
directives: [NgFor, BeerItem]
})
class MyAppComponent {
public beers: String[] = [];
constructor() { ... }
}
Support des WebComponents
WebComponents
@Component({ })
@View({
template: string,
templateUrl: string,
encapsulation?: ViewEncapsulation,
directives?: List<any>,
styles?: List<string>,
styleUrls?: List<string>,
})
class BeerItem { }
WebComponents
//From modules/angular2/src/core/render/api.ts
/**
* How the template and styles of a view should be encapsulated.
*/
export enum ViewEncapsulation {
/**
* Emulate scoping of styles by preprocessing the style rules
* and adding additional attributes to elements. This is the default.
*/
Emulated,
/**
* Uses the native mechanism of the renderer. For the DOM this means creating a
* ShadowRoot.
*/
Native,
/**
* Don't scope the template nor the styles.
*/
None
}
Injection de Dépendance
Injection de Dépendances
- Code métier dans des services
- Chaque Service est un singleton
- Principe d'Hollywood
- Multiples implémentations en NG1 !
Injection de Dépendances
app.service('TapService', function($http){
this.getBeer = function(beerId){
return $http.get('/api/i-am-thirsty/' + beerId);
};
});
app.controller('AppCtrl', function(TapService){
this.selectBeer = function(idBeer){
return TapService.getBeer(idBeer);
}
});
DI version Angular2
- 1 Injecteur principal + 1 Injecteur par composant
- Hérite de l'injecteur parent
- Possibilité de redéfinir le Service à injecter
- Utilisation d'annotations en ES6 et des types en TypeScript
- Services disponibles via le constructeur du composant
Injecteur Principal - toValue
@Component({selector: 'my-app'})
@View({
template: `<main>
<h1> Welcome to our {{breweryName}}</h1>
</main>`
})
class MyAppComponent {
constructor(private breweryName:String) { ... }
}
bootstrap(MyAppComponent, [bind(String).toValue('Zenika Brewery')]);
Injecteur Principal - toClass
@Component({selector: 'my-app'})
@View({
template: `<main>
<h1> Welcome to our {{breweryName}}</h1>
</main>`
})
class MyAppComponent {
constructor(private breweryService:BreweryService) {
this.breweryName = this.breweryService.getBreweryName();
}
}
bootstrap(MyAppComponent, [bind(BreweryService).toClass(BreweryService)]);
Injecteur Principal - toClass
@Component({selector: 'my-app'})
@View({
template: `<main>
<h1> Welcome to our {{breweryName}}</h1>
</main>`
})
class MyAppComponent {
constructor(private breweryService:BreweryService) {
this.breweryName = this.breweryService.getBreweryName();
}
}
bootstrap(MyAppComponent, [BreweryService]);
Injecteur Principal - toFactory
@Component({selector: 'my-app'})
@View({
template: `<main>
<h1> Welcome to our {{breweryName}}</h1>
</main>`
})
class MyAppComponent {
constructor(public breweryName:String) { ... }
}
bootstrap(MyAppComponent,
[bind(String)
.toFactory((BreweryService) => {
return BreweryService.getBreweryName();
}, [BreweryService])]);
Child Injector
@Component({selector: 'my-app'})
@View({
template: `<main>
<welcome-message></welcome-message>
</main>`,
directives: [WelcomeMessage]
})
class MyAppComponent {
constructor(public breweryName:String) { ... }
}
bootstrap(MyAppComponent, [bind(String).toValue('Zenika Brewery')]);
Child Injector
@Component({
selector: 'welcome-message'
})
@View({
template: `<h1>Welcome to our {{breweryName}}</h1>`
})
class WelcomeMessage {
constructor(public breweryName:String) { ... }
}
Child Injector
@Component({
selector: 'welcome-message'
})
@View({
template: `<h1>Welcome to our {{breweryName}}</h1>`,
bindings: [
bind(String).toValue('Awesome Zenika Brewery')
]
})
class WelcomeMessage {
constructor(public breweryName:String){ ... }
}
Pipes
Petit Rappel
{{ collectionOfBeers | orderBy:'note' | limitTo:5 }}
Pipes
- Identiques aux filtres d'AngularJS 1
- Permet de manipuler une donnée
- Utilisation d'une classe annotée @Pipe
- Pipes disponibles dans le framework :
- upperCase, lowerCase, async, number, slice, json et date
Pipes
import {Pipe, PipeTransform} from 'angular2/angular2';
@Pipe({
name: 'UpperCasePipe'
})
export class UpperCasePipe implements PipeTransform {
transform(value: String, args: any[]) {
return value.toUpperCase();
}
}
Pipes
import {Component, View} from 'angular2/angular2';
import {UpperCasePipe} from './UpperCasePipe.ts'
@Component({
selector: 'widget1'
})
@View({
template: `<div>{{'Démo utilisant les pipes' | UpperCasePipe}}</div>`,
pipes: [UpperCasePipe]
})
export class Widget1{}
Pipes
import {Component, View} from 'angular2/angular2';
import {UpperCasePipe} from './UpperCasePipe.ts'
@Component({
selector: 'widget1'
})
@View({
template: ``,
bindings: [UpperCasePipe]
})
export class Widget1{
constructor(public upperCasePipe:UpperCasePipe){
this.upperCasePipe.transform('Un autre exemple...');
}
}
@Component
@View
@Directive
@Animation
@Inject
@InjectLazy
@Optional
@Host
@Parent
@Pipe
@Property
@Event
@RouteConfig
@HostBinding
@HostEvent
@ContentChild
@ContentChildren
@ViewChild
@ViewChildren
@Component
@View
@Directive
@Animation
@Inject
@InjectLazy
@Optional
@Host
@Parent
@Pipe
@Property
@Event
@RouteConfig
@HostBinding
@HostEvent
@ContentChild
@ContentChildren
@ViewChild
@ViewChildren
Roadmap
ngUpgrade.js
Mixer AngularJS et Angular2
Démo
https://github.com/Gillespie59/angular2-migration-sample
Questions ?
Kaiz'n Day
By Emmanuel Demey
Kaiz'n Day
- 1,697