Angular 2
@EmmanuelDemey
Emmanuel DEMEY
Consultant & Formateur Web
Zenika Lille
@EmmanuelDemey
Web / Domotique / Biérologie
Préparer votre application à Angular2
John Papa's styleguide
Service AngularJS
- Utiliser la méthode service
- Similaire à l'implémentation Angular2
angular.module('MyApp')
.service('BreweryService', function(){
this.getBeers(){ ... }
});
Utilisation de ngAnnotate
angular.module('MyApp')
.controller('BreweryCtrl',
['BreweryService',
function(BreweryService){
BreweryService.getBeers();
}]);
function BreweryCtrl(BreweryService){
BreweryService.getBeers();
}
BreweryCtrl.$inject = ['BreweryService']
angular.module('MyApp')
.controller('BreweryCtrl', BreweryCtrl);
Utilisation de ngAnnotate
- Eviter les problèmes après Minification
- Plus de duplication de code
- Intégration Grunt/Gulp
angular.module('MyApp')
.controller('BreweryCtrl',
function(BreweryService){
BreweryService.getBeers();
});
Component-First Approach
- Se baser sur des composants simples et réutilisables
- Bannir l'utilisation des ng-controller
- Architecture identique à Angular2
- Eviter les contrôleurs et templates trop longs
controllerAs / bindToController
- Eviter d'utiliser le scope
- dans les contrôleurs
- dans les directives
- Faciliter la hiérarchie des contrôleurs
angular.module('MyApp')
.controller('BreweryCtrl',
function(){
this.beers = [];
});
<ul ng-controller="BreweryCtrl as bc">
<li ng-repeat="beer in bc.beers">
{{beer}}
</li>
</ul>
ES2015 / TypeScript
- Définition des composants AngularJS via des classes
- Utilisation des modules ES2015
class BreweryCtrl {
constructor() { ... }
selectBeer() { ... }
}
export { BreweryCtrl }
import { BreweryCtrl} from './BreweryCtrl';
angular
.module('app', [])
.controller('BreweryCtrl', BreweryCtrl);
Autres Bonnes Pratiques
- 1 composant AngularJS par fichier
- Utilise le nouveau Router
- Utilisation de l'API Component
- Utilisation AngularJS > 1.5
- ...
Mise en pratique
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(...) { ... }
}
});
<input ng-model="firstName">
<p>
{{firstName }}
</p>
2-way data-binding
Et ce n'est pas fini...
Mais aussi...
- JavaScript
- Hiérarchie des scopes
- Pas de Server-Side Rendering
- Gestion des événements (ngClick, ...)
- Gestion des attributs HTML (ngSrc, ...)
La solution... Angular 2
- 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 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
import {Component} from '@angular/core';
@Component({
selector: 'my-app',
template: `<main>
<h1> This is my first Angular2 app</h1>
</main>`
})
class MyAppComponent {
}
Composant version TypeScript
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { platformBrowserDynamic }
from '@angular/platform-browser-dynamic';
import { MyAppComponent } from './app.component';
@NgModule({
declarations: [
MyAppComponent
],
imports: [
BrowserModule
],
bootstrap: [MyAppComponent]
})
export class AppModule { }
platformBrowserDynamic().bootstrapModule(AppModule);
Bootstrap de l'Application
Binding
Root Cpt
Child1 Cpt
Child2 Cpt
[property]="expression"
(event)="update()"
Property Binding
<input [property]="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
//<beer-item [beer]="'Maredsous'"></beer-item>
@Component({
selector: 'beer-item',
template: `<section>
<h2>{{beer}}</h2>
<button>Je veux celle-ci !</button>
</section>`
})
export class BeerItem {
@Input() 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
//<beer-item [beer]="'Maredsous'" (selectBeer)="sendToBeerTap($event)"></beer-item>
@Component({
selector: 'beer-item',
template: `<section>
<h2>{{beer}}</h2>
<button (click)="selectItem()">Je veux celle-ci !</button>
</section>`
})
export class BeerItem {
@Input() beer: string;
@Output() selectBeer: EventEmitter = new EventEmitter<string>();
selectItem() {
this.selectBeer.next(this.beer);
}
}
Injection de Dépendance
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 - useClass
@Component({
selector: 'my-app',
template: `<main><h1> Welcome to our {{breweryName}}</h1></main>`
})
class MyAppComponent {
breweryName:string;
constructor(private breweryService:BreweryService) {
this.breweryName = this.breweryService.getBreweryName();
}
}
//module.ts
@NgModule({
providers: [BreweryService]
})
export class AppModule { }
Injecteur Principal - useValue
@Component({
selector: 'my-app',
template: `<main>
<h1> Welcome to our {{breweryName}}</h1>
</main>`
})
class MyAppComponent {
constructor(@Inject('title') public breweryName:string) { ... }
}
//module.ts
@NgModule({
providers: [{ provide: 'title', useValue: 'Zenika Brewery' }]
})
export class AppModule { }
Injecteur Principal - useClass
@Component({
selector: 'my-app',
template: `<main>
<h1> Welcome to our {{breweryName}}</h1>
</main>`
})
class MyAppComponent {
constructor(public breweryService:BreweryService) { ... }
}
//module.ts
@NgModule({
providers: [{ provide: BreweryService, useClass: BBLBreweryService }]
})
export class AppModule { }
Child Injector
@Component({
selector: 'my-app',
template: `<main>
<welcome-message></welcome-message>
</main>`
})
class MyAppComponent {
constructor(@Inject('title') public breweryName:string) { ... }
}
//module.ts
@NgModule({
declarations: [
MyAppComponent, WelcomeMessage
],
providers: [[{ provide: 'title', useValue: 'Zenika Brewery' }]
})
export class AppModule { }
Child Injector
@Component({
selector: 'welcome-message',
template: `<h1>Welcome to our {{breweryName}}</h1>`
})
export class WelcomeMessage {
constructor(@Inject('title') public breweryName:string) { ... }
}
Child Injector
@Component({
selector: 'welcome-message',
template: `<h1>Welcome to our {{breweryName}}</h1>`,
providers: [
[{ provide: 'title', useValue: 'Awesome Zenika Brewery' }]
]
})
export class WelcomeMessage {
constructor(@Inject('title') 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/core';
@Pipe({
name: 'uppercase'
})
export class UpperCasePipe implements PipeTransform {
transform(value: string, args: any[]) {
return value.toUpperCase();
}
}
Pipes
import {Component} from 'angular2/core';
import {UpperCasePipe} from './UpperCasePipe'
@Component({
selector: 'widget1',
template: `<div>{{'Démo utilisant les pipes' | uppercase}}</div>`
})
export class Widget1{}
Démo
Questions ?
BBL
By Emmanuel Demey
BBL
- 1,940