Ambient IT - Academy 🤖
Romain Signes 🚀
Angular
ES6, le nouveau JavaScript
ES2015 === ES6 === ECMAScript 6
Ambient IT - Academy 🤖
Romain Signes 🚀
L'histoire d'ECMAScript
Compatibilité
Transpilation
Fonctionnalités notables
- let & const
- arrow functions
- modules
- class
- spread & rest
- paramètres optionnels & valeurs par défaut
- template strings
- Promise
let & const
let x = 1;
{
let x = 2;
}
console.log(x);
const x = 1;
x = 2;
1
ERROR
arrow functions
function Poney() {
const button = document.getElementById('run');
button.addEventListener('click', () => {
this.handleClick();
});
}
'use strict';
function Poney() {
var _this = this;
var button = document.getElementById('run');
button.addEventListener('click', function () {
_this.handleClick();
});
}
TRANSPILATIOOOOOOOOOOOON
modules
import { Poney } from '../interfaces/poney';
import Race from '../interfaces/race';
import * as lodash from 'lodash';
export Poney;
export default Race;
Import
Export
class
export class Poney {
constructor(name) {
this.name = name;
}
run() {
// TODO : run function
}
}
spread & rest
function addPonies(...ponies) {
for (let pony of ponies) {
this.poniesInRace.push(pony);
}
}
...spread déstructure un tableau ou objet
function addPoney(poney) {
this.poniesInRace = [...this.poniesInRace, poney];
}
...rest le restitue
Paramètres optionnels
// Valeurs par défaut
function getPonies(size = 10, page = 1) {
server.get(size, page);
}
// Les calculs de valeur par défaut sont possibles
function getPonies(size = defaultSize(), page = size - 1) {
server.get(size, page);
}
template strings
// Concaténation à l'ancienne
const fullname = 'Miss ' + firstname + ' ' + lastname;
// Equivalent en template string
const fullname = `Miss ${firstname} ${lastname}`;
Promise
// Créer un object Promise
const getUser = function (login) {
return new Promise(function (resolve, reject) {
// opération asynchrone, appel au serveur HTTP...
if (response.status === 200) {
resolve(response.data);
} else {
reject('No user');
}
});
};
// Utiliser un objet Promise
getUser(login)
.then(user => {
return getRights(user);
})
.then((rights) => {
updateMenu(rights);
})
.catch(handleError)
Typage statique et Typescript
Ambient IT - Academy 🤖
Romain Signes 🚀
Les bases de Typescript :Â
Caractéristiques:
- syntaxe proche de Javascript
- extension .tsÂ
const fooNumber: number = 0;
const fooString: string = 'Blah !';
// Types génériques
const fooArray: Array<string> = ['Blah !'];
// Custom types
const fooType: FooType = new FooType()
const fooTypeArray: Array<FooType> = [new FooType()];
Les bases de Typescript :Â
Concrètement :
const fooBlah: Array<string>
this.fooBlah.push('Blah blah')
// Tout fonctionne correctement
this.fooBlah.push({parler: true, contenu: 'Blah blah'})
// error TS2345
// Argument of type {} is not assignable to parameter of
// type 'string'.
Les bases de Typescript :Â
Et si on ne connaît pas le type ?
=> type dynamique "any"
const undefinedType: any
On peut aussi utiliser l'union de types :Â
const undefinedType: string | number
undefinedType = 'Blah'
undefinedType = 25 // ✌️
Que peut-on typer ?
let n: number = 1
const s: string = 'Hello'
Paramètres de fonctions :Â
function f(i: number) { ... }
Retour de fonction :Â
function f(): number {
return 42
}
Variables :
Types basiques :
Booleans:Â
let fini: boolean = false
Numbers:
Nb : En plus des décimales et hexadécimales, Typescript supporte aussi les types de littéraux binaires et octaux introduits par ES6.​
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
Types basiques :
Strings:
let color: string = "blue";
color = 'red';
Nb : Typescript supporte aussi les "template strings", introduites par ES6.​
let fullName: string = `Bob Bobbington`;
let age: number = 37;
let sentence: string = `Hello, my name is ${ fullName }.
I'll be ${ age + 1 } years old next month.`;
Types basiques :
Arrays :
Deux façons de typer les arrays :
let list: number[] = [1, 2, 3]; //elemType[]
let list: Array<number> = [1, 2, 3]; // Array<elemType>:
Tuple :Â
Permet de typer un tableau où le type de certains éléments est connu :
// Declare a tuple type
let x: [string, number];
// Initialize it
x = ["hello", 10]; // OK
// Initialize it incorrectly
x = [10, "hello"]; // Error
Types implicites :
Lorsqu'il s'agit de types basiques, le compilateur associe par défaut les types des variables non annotées :
let n = 1 // let n: number = 1
let s = "Hello World" // let s: string = "Hello World"
n = s; // COMPILATION ERROR
s = n; // COMPILATION ERROR
function f() { // function f(): string {
return "hello"
}
Les bases de Typescript :Â
DĂ©clarer un custom type :
=> créer une classe ou une interface
interface Animal {
nom: string;
bruit: string;
}
function anime(arg: Animal)
{ return `${arg.name} fait ${arg.bruit}`}
const Canard: Animal = {nom: 'Canard', bruit: 'coin coin'}
anime(Canard) // Ok
Les bases de Typescript :Â
Classes et interfaces :
Un type peut ĂŞtre dĂ©clarĂ© dans Typescript par une classe ou une interface :Â
interface Poulet {
race: string;
pleinAir: boolean;
fermier: boolean;
}
class Canard {
constructor( public race: string,
public fermier: boolean,
public magret: boolean) {}
}
Les bases de Typescript :Â
Classes et interfaces :
DiffĂ©rence --> une interface ne peut pas ĂŞtre instanciĂ©e :Â
const poulet : Poulet = {
race: 'Gallus domesticus',
pleinAir: true,
fermier: true
}
const canard: Canard = new Canard ('Canard de Barbarie',
true, true)
Les bases de Typescript :Â
Nb - Le réel intérêt des interfaces :
Â
Interface (classe 'allégée') => intérêt visible après compilation:
Â
- l'interface sert à vérifier les types --> effacée de l'output final
- la classe, mĂŞme non instanciĂ©e, est considĂ©rĂ©e comme dĂ©clarĂ©e --> prĂ©sente dans l'output final.Â
Les bases de Typescript :Â
Les décorateurs :
Â
Déclarations particulières :
- propres Ă Typescript
- attachées à des classes, méthodes, paramètres, etc.
Â
Syntaxe: @expression ('expression' --> une fonction appelĂ©e Ă l'instanciation de l'Ă©lĂ©ment dĂ©corĂ©)Â
Â
En Angular:  attacher des "métadonnées" propres au framework
Les bases de Typescript :Â
Les décorateurs :
Â
Un exemple de décorateur très commun : le composant !
import { NgModule, Component } from '@angular/core';
@Component({
selector: 'composant-exemple',
template: '<div>Woow un composant !</div>',
})
export class ExempleComposant {
constructor() {
console.log('Hello, je suis un composant');
}
}
Les bases de Typescript :Â
Les décorateurs :
Â
Décorateur = fonction exécutée lors de l'instanciation de la classe
Â
=> ici permet Ă Angular de :
- définir 'ExempleComposant' comme composant
- configurer 'ExempleComposant'Â
Les bases de Typescript :Â
TP :
Â
Installer le compilateur Typescript: npm install -g typescript
Â
Créer un fichier en Ts :
- déclarer une interface + instancier un objet l'implémentant
- instancier une classe + instancier un objet de cette classe
- utiliser ces objets dans une méthode avec un feature ES6 (helper functions, template litterals...)
Â
Puis compiler en Js: tsc nomDuFichier.ts
Les bases du framework
Ambient IT - Academy 🤖
Romain Signes 🚀
Comprendre la philosophie du framework
Angular ("Angular 2") est un framework :
- placé côté client
- fonctionnel sur navigateur, web workers, mobiles, serveurs (Angular Universal)
Â
Release : développé par l'Angular Team et paru en septembre 2016.
Â
Nb: Il s'agit d'une refonte totale d'AngularJS (créé en 2009) avec lequel il ne doit pas être confondu.
Framework cĂ´tĂ© clientÂ
Différence framework front / back :
- back: navigateur construit le DOM en "parsant" un document HTML prĂŞt Ă ĂŞtre rendu
- front: navigateur construit le DOM en interprétant un script
Â
Avantages : limite les intéractions serveurs
=> navigation très fluide (en particulier en web mobile).
Â
Inconvénient : Un chargement au démarage qui peut être long (conseillé < 250kb)
RĂ©ponse serveur au lancement d'une application Angular
Text
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Angular Sample App</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
// Le composant racine de notre application
<app-root></app-root>
// L'ensemble des scripts permettant la construction du DOM Ă partir du composant root
<script type="text/javascript" src="inline.bundle.js"></script>
<script type="text/javascript" src="polyfills.bundle.js"></script>
<script type="text/javascript" src="scripts.bundle.js"></script>
<script type="text/javascript" src="styles.bundle.js"></script>
<script type="text/javascript" src="vendor.bundle.js"></script>
<script type="text/javascript" src="main.bundle.js"></script>
</body>
</html>
Le concept des SPA
Single Page Application :
une fois instanciée au chargement, l'application n'a plus besoin de reload auprès du server pour fonctionner.
Â
Â
Questions
Â
Est-ce qu'on peut avoir plusieurs pages sur une SPA ?
Angular => module Router, permet de "simuler" différentes Urls.
Exemple d'instanciation d'un Router
Le Router est instanciĂ© dans le fichier src/app/app.module.ts :Â
import { RouterModule, Routes } from '@angular/router';
// autres imports
const appRoutes: Routes = [
{ path: 'route1', component: Route1Component },
// on peut aussi inclure des paramètres dans l'url
{ path: 'route2/:parametre2', component: Route2Component },
{ path: '**', component: PageNotFoundComponent }
];
@NgModule({
imports: [
RouterModule.forRoot(appRoutes)
// autres imports
],
...
})
export class AppModule { }
Angular CLI, un outil pour tout générer
L'Angular CLI :
- ng new +nom : boilerplate
- ng generate (ng g) : nouveaux éléments
- ng serve : serveur de développement
- ng build : fichiers distants
- etc...
Â
Installation :
npm install -g @angular/cli
Création de votre première application Angular
ExtrĂŞmement simple :
ng new applicationAngular
cd applicationAngular
ng serve
=> http://localhost:4200Â Â \^^/
Architecture standard d'une application Angular
// Tout ce qui va concerner les tests end to end
|- e2e/
|----- app.e2e-spec.ts
|----- app.po.ts
|----- tsconfig.e2e.json
// les dépendances avec npm
|- node_modules/
// l'endroit oĂą les fichiers de build seront mis
|- dist/
// Le dossier oĂą vous allez modifier vos fichiers de code
//LĂ oĂą va se trouver vos composants, services, etc..
|- src/
|----- app/
|----- app.component.css|html|spec.ts|ts
|----- app.module.ts
|----- assets/
|----- environments/
|----- environment.prod.ts|ts
|----- favicon.ico
|----- index.html
|----- main.ts
|----- polyfills.ts
|----- styles.css
|----- test.ts
|----- tsconfig.app.json
|----- tsconfig.spec.json
|----- typings.d.ts
Architecture standard d'une application Angular
// la configuration globale de votre application
|- .angular-cli.json // fichier de configuration principal
|- .editorconfig // peut être utilisé dans VS Code setups
|- .gitignore
|- karma.conf.js
|- package.json
|- protractor.conf.js
|- README.md
|- tsconfig.json
|- tslint.json
Les principaux fichiers
app.module.ts :
Â
/* imports JavaScript */
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppComponent } from './app.component';
/* classe AppModule avec le décorateur @NgModule */
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Les principaux fichiers
app.module.ts :
Â
- référencer tous les imports
- instancier une nouvelle classe NgModule
=> métadata : configurent la compilation Angular des différents éléments
@NgModule({
declarations: [...],
imports: [...],
providers: [...],
bootstrap: [...]
})
export class AppModule { }
Les principaux fichiers
app.module.ts :
Â
DĂ©tail de l'objet paramètre de @NgModule :Â
- declarations— les composants de l'application
- imports—les modules importés (BrowserModule permettant de rendre l'application dans un navigateur, etc)
- providers—les providers de services
- bootstrap—le composant root que Angular créé et insert dans l'index.html (point d'entrée) et permettant l'initialisation de l'application
Nb: AppComponent, composant root par défaut, est présent dans declarations et bootstrap.
Les principaux fichiers
app.component.ts - notre premier composant !
Â
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app';
}
Les principaux fichiers
app.component.ts - notre premier composant !
Â
<!doctype html>
<html lang="en">
<head><title>Une app Angular</title>...</head>
<body>
<!-- appComponent instancié ici ! -->
<app-root></app-root>
</body>
</html>
AppComponent:
- seul composant présent dans index.html
- instanciera 'en cascade' l'ensemble de l'app
Les principaux fichiers
app.component.ts - notre premier composant !
Â
Une cascade de composants :
<app-composant1></app-composant1>
<app-composant2></app-composant2>
<app-composant3></app-composant3>
Nb: n'importe quel composant peut instancier un composant 'enfant' - pas réservé au composant racine.
Composants
Ambient IT - Academy 🤖
Romain Signes 🚀
COMPOSANTS
Web Components
DĂ©corateurs Angular
Property binding, envoyer des données au composant
Event binding, évènements personnalisés
Cycle de vie
TP : Premier composant
Web components
Principe:
permettent la crĂ©ation de nouveaux tags HTML personnalisĂ©sÂ
Â
API:
CustomElementRegistry.define(), avec en arguments :
- nom de l'élément
- un objet de classe définissant le comportement de l'élément
(- options facultatives)
Web components
Nb: fonctionalité implémentée nativement dans certains navigateurs
Â
- pas de librairies, framework...
- pas entièrement supportée (polyfills)
Web components
customElements.define('word-count', WordCount,
{ extends: 'p' });
class PopUpInfo extends HTMLElement {
constructor() {
// Toujours appeler "super" d'abord dans le constructeur
super();
// Ecrire la fonctionnalité de l'élément ici
...
}
}
Composants Angular :
Reprennent le mĂŞme principe (custom tags) + ajout de nombreuses API (services, routing, communication server, etc).Â
Â
Nb: En réalité, composants webs et angular diffèrent car:
- composants webs => l'API des custom elements (fonction define(), ..)
- composants Angular => propre système de création d'éléments custom (ngFactories).
Composants Angular :
@Component({
selector: 'greet',
template: 'Hello {{name}}!'
})
class Greet {
name: string = 'World';
}
// Rendu HTML
<app-great>Hello World!</app-great>
Un nouveau composant :
ng g c exemple
Création d'un nouveau composant :
La CLI se charge de :
- création de 4 fichiers :
exemple.component.ts | html | css |spec.ts (fichier de test) - configuration du composant dans NgModule
Un nouveau composant :
...
import { ExempleComponent } from './exemple/exemple.component';
@NgModule({
declarations: [..., ExempleComponent],
imports: [...],
providers: [...],
bootstrap: [...]
})
export class AppModule { }
Fichier app.module.ts actualisé : ​
import {Component} from '@angular/core';
@Component({
selector: 'un-composant',
template: `<h1>Wow - un composant !</h1>`
})
class UnComposant {}
DĂ©corateurs composants
<un-composant></un-composant>
Un composant est une classe pourvue d'un décorateur @Component({}) :
@Component({
changeDetection?: ChangeDetectionStrategy
viewProviders?: Provider[]
moduleId?: string
templateUrl?: string
template?: string
styleUrls?: string[]
styles?: string[]
animations?: any[]
encapsulation?: ViewEncapsulation
interpolation?: [string, string]
entryComponents?: Array<Type<any> | any[]>
preserveWhitespaces?: boolean
// inherited from core/Directive
selector?: string
inputs?: string[]
outputs?: string[]
host?: {...}
providers?: Provider[]
exportAs?: string
queries?: {...}
})
DĂ©corateurs composants
Le décorateur @Component({}) permet de configurer le composant en passant des métadata en paramètre au décorateur :
import {Component} from '@angular/core';
@Component({
selector: 'un-composant',
template: `<h1>Wow - un composant !</h1>
<div>Je suis un composant</div>`,
styles: [`h1 { color: red}
div {color: blue}`]
})
class UnComposant {}
DĂ©corateurs composants
En pratique, on se sert principalement des paramètres selector, template (ou templateUrl) et styles (ou styleUrls) :
DĂ©corateurs composants
Wow - un appel Ă notre nouveau composant :
<app-exemple></app-exemple>
Et instanciation de notre nouveau composant, dans app.component.html par exemple :Â
@Component({
selector: 'no-encapsulation',
templateUrl: './no-encapsulation.html',
styleUrls: ['./no-encapsulation.css'],
encapsulation: ViewEncapsulation.None // Supprime le principe d'encapsulation
})
class NoEncapsulation {}
DĂ©corateurs composants
Question : Le style défini dans un composant parent sera-t-il appliqué à un composant enfant ?
Non => principe de View Encapsulation, ( composant = vue isolée)
Pour forcer l'hĂ©ritage :Â
Cycles de vie
Lifecycle hooks :
@Component(...)
export class MyComponent {
constructor() { }
ngOnInit() {}
ngOnDestroy() {}
ngDoCheck() {}
ngOnChanges(records) {}
ngAfterContentInit() {}
ngAfterContentChecked() {}
ngAfterViewInit() {}
ngAfterViewChecked() {}
}
Permettent d'appeler des callbacks à différents moments du cycle.
Caractéristiques composant
Lifecycle hooks :
import {Component, OnInit} from '@angular/core'
@Component()
export class myComponent implements OnInit{
constructor() {}
ngOnInit() { /* code Ă executer Ă l'initialisation */ }
}
En pratique, on utilise principalement ngOnInit() en addition au constructeur :
Nb : ne pas oublier d'importer et d'implémenter le hook.
Data binding
Ambient IT - Academy 🤖
Romain Signes 🚀
Data binding
échanger des données
Angular nous permet d'Ă©changer des donnĂ©es entre :Â
Â
- le composant et la vue
   - one way (vue => composant // composant => vue)
   - two ways (vue <=> composant)
- entre les différents composants
   - par input (parent => enfant)
   - par output (enfant => parents & siblings)
Data binding
composant <=> vue
Un grand avantage des composants est de nous permettre de manipuler des donnĂ©es dans le DOM - pour cela, nous avons besoin de pouvoir lier les datas entre le composant (plus exactement, son instance, dĂ©clarĂ©e dans le fichier .ts - que l'on peut assimiler au 'Model') et son template (la 'Vue').Â
Â
Pour cela, Angular met Ă notre disposition plusieurs façons de 'binder' les datas.Â
composant / vue: one-way
​A. Interpolation : {{ .. }}
La syntaxe {{ var }} permet de lier d'inclure une variable déclarée dans le composant dans le template
@Component({
selector: 'app-exemple',
template: 'Je suis une {{ foo }}'
})
export class ExempleComponent {
foo : string = 'interpolation'
}
1. Composant => vue :
composant <=> vue: one way
​B. Property binding : [ .. ]
La syntaxe [prop] = "var" permet de lier la propriété d'un attribut d'un élément du DOM à une variable.
@Component({
selector: 'app-exemple',
template: '<div [property]="binding">
Une div avec un attribut bindé
</div>'
})
export class ExempleComponent {
binding : string = 'nouvelle valeur'
}
composant <=> vue: one way
​Event binding : ( .. )
La syntaxe (event) = "methode()" permet de lier un événement provenant de la vue à une variable ou méthode déclarée dans le composant.
2. Vue => composant :
(focus)="myMethod()" // An element has received focus
(blur)="myMethod()" // An element has lost focus
(submit)="myMethod()" // A submit button has been pressed
(scroll)="myMethod()"
(cut)="myMethod()"
(copy)="myMethod()"
(paste)="myMethod()"
(keydown)="myMethod()"
(keypress)="myMethod()"
(keyup)="myMethod()"
(mouseenter)="myMethod()"
(mousedown)="myMethod()"
(mouseup)="myMethod()"
(click)="myMethod()"
(dblclick)="myMethod()"
(drag)="myMethod()"
(dragover)="myMethod()"
(drop)="myMethod()"
composant <=> vue: two ways
La directive ngModel : [( ngModel )]
La syntaxe [(ngModel)] = "variable" permet de lier la valeur d'un input à une variable d'un composant - qui peut ensuite être renvoyée actualisée à la vue.
Â
En réalité, ngModel est une combinaison de l'event binding et de la property binding.
2. Vue => composant => vue :
composant <=> vue: two ways
La directive ngModel : [( ngModel )]
<div>
<!-- la variable 'model' est updatée à chaque fois que
l'utilisateur modifie la valeur de l'input, et
updatée dans le composant
-->
<input [(ngModel)]="model">
<!-- le composant 'renvoie' la valeur actualisée
Ă la vue
-->
<p>Valeur actualisée : {{model}}</p>
</div>
composant <=> vue: two ways
La directive ngModel : Implémentation
// app.module.ts
import { FormsModule } from '@angular/forms';
@NgModule({
imports: [ FormsModule, ...],
... })
Nb: ngModel nĂ©cessite l'import prĂ©alable de FormsModule :Â
composant <=> vue: two ways
La directive ngModel : Implémentation
// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `<input [(ngModel)]="model">
<p>Notre data-binding : {{model}}</p>`
})
export class AppComponent {
model: string; // DĂ©claration de la variable de binding
}
Data binding
composant <=> vue
TP 1:
Â
Créer un composant avec:
- une interpolation (controller => vue)
- un property-binding (controller => vue)
- un event-binding (vue => controller)
- un ng-model (controller <=> vue)
Data binding
parent <=> enfant
Parent (controller / vue)Â => enfant (vue)
- principe de transclusion
Â
Parent (controller / vue)Â => enfant (controller / vue)
- property / event bindings (parent)
- @Input / @Output (enfant
Â
Nb: Parents / enfants directs uniquement.
Inter-comp :Â
Transclusion : Parent (controller / vue) => enfant (vue)
=> 'slot' pour insertion dynamique de contenu
@Component({
selector: 'app-transclusion',
templateUrl: './transclusion.component.html'
})
export class TransclusionComponent {}
<!-- transclusion.component.html -->
<div class="transclusion-header">Je suis un header fixé</div>
<ng-content></ng-content> <!-- contenu dynamique ici -->
<div class="transclusion-header">Je suis un footer fixé</div>
Inter-comp :Â
Transclusion : Parent (controller / vue) => enfant (vue)
Instanciation dans le composant parent :
<!-- parent.component.html -->
<p>Intégration de contenu dynamique :</p>
<transclusionComponent>
Wow - du contenu intégré dynamiquement !
</transclusionComponent>
Inter-comp :Â
Transclusion : Parent (controller / vue) => enfant (vue)
Output :Â
<p _ngcontent-c0="">Intégration de contenu dynamique :</p>
<app-transclusion _ngcontent-c0="" _nghost-c1="">
<div _ngcontent-c1="" class="transclusion-header">
Je suis un header fixé</div>
Wow - du contenu intégré dynamiquement !
<div _ngcontent-c1="" class="transclusion-header">
Je suis un footer fixé</div>
</app-transclusion>
Inter-comp :Â
Input : Parent (controller / vue) => enfant (controller)
Principe:Â
- instanciation enfant avec donnée à transmettre en attribut
- 'reception' de l'attribut par controller enfant via @Input
Inter-comp :Â
Input : Parent (controller / vue) => enfant (controller)
Instanciation enfant :
// Data non liée au controller parent
<child data="dataFromParent"></child>
// Data liée au controller parent
<child [data]="dataFromParent"></child>
Inter-comp :Â
Input : Parent (controller / vue) => enfant (controller)
Controller enfant :
// Ne pas oublier d'importer le décorateur Input
import { Component, Input} from '@angular/core';
@Component({
selector: 'child',
...
})
export class Child {
@Input() item: WineItem;
}
Inter-comp :Â
Output : Enfant (controller) => parent (vue puis controller)
Principe :
- Ă l'instanciation, lier un 'event' enfant Ă un callback parent
Â
=> envoi de donnĂ©es:Â
- l'enfant Ă©met un 'event'
- l'event déclenche un callback parent
- le parent est notifié de la donnée
Â
Inter-comp :Â
Output : Enfant (controller) => parent (vue puis controller)
Composant parent :
// ParentComponent.html
<child (receivedData)="newDataCallback($event)"></child>
// ParentComponent.ts
@Component({ ...})
export class ParentComponent {
data: any;
newDataCallback(event: any): void {
// update d'une variable locale
this.data = event
}
}
Inter-comp :Â
Output : Enfant (controller) => parent (vue puis controller)
Controller enfant :
import { Component, Output, EventEmitter } from '@angular/core';
@Component({ selector: 'child',... })
export class ItemDetailsComponent {
// Lier la propriété 'dataToSend' à l'attribut 'receivedData'
@Output('receivedData') dataToSend: EventEmitter<any>;
constructor() {
this.dataToSend = new EventEmitter<ISelectEvent>()
}
sendData(data): void { this.itemSel.emit({ data }) }
}
Data binding
Parent <=> Enfant(s)
TP 2:
Â
Créer un ensemble composant parent / enfant avec :
- une transclusion (vue parent => vue enfant)
- un input statique et dynamique (vue/controller parent => controller enfant)
- un output (controller enfant => controller parent) avec un objet de données (ex: click + origine)
Directives
Ambient IT - Academy 🤖
Romain Signes 🚀
Les directives :
Le problème :
Composants => créer des vues dans de le DOM
Â
Mais ne permettent pas de modifier des éléments existants du DOM, comme :
- structure des noeuds
- comportement des Ă©lements
Â
Les directives :
DĂ©finition :
Classe associée à une balise (idem composant), mais sans vue.
Nb : composants = directives particulières jusqu'à Angular 2.
Â
Intérêt :
Faire exécuter du code au navigateur modifiant des éléments présents dans le DOM.
Les directives :
import { Directive, Renderer2, ElementRef, HostListener } from '@angular/core';
@Directive({selector: '[appHoverEffect]'})
export class HoverEffectDirective {
constructor( private renderer : Renderer2, private elementRef: ElementRef ) { }
@HostListener('mouseover')
applyHover() {
this.renderer.setStyle(this.elementRef.nativeElement, 'color', 'red')}
}
DĂ©claration :
Les directives :
// Element standard du DOM
<div appHoverEffect> App hover ! </div>
// Autre
<directedComponent appHoverEffect><directedComponent>
Instanciation :
Nb: nouvelle instance de directive créée à chaque rencontre de la balise.
Les directives :
Exemples d'utilisation:
- itéreration d'un élément DOM existant sur une liste - structure
- ajout / retrait dynamique d'un nœud - structure
- règles CSS à des éléments - comportement
--> pourquoi une directive plutĂ´t qu'une classe ?
- réponse à un événement - comportement
- etc...
Les directives :
Types de directives :
- les directives structurelles : modifient la structure du DOM
- les directives d'attributs : modifient l'apparence ou le comportement de certains éléments
Â
Directives structurelles :
Syntaxe : *directive = "template expression"
- ' * ' : obligatoire pour directives structurelles -Â Nb : contrairement aux directives d'attribut, pas de [..] ou de (..)
- 'template expression' : expression retournant une valeur Ă©valuĂ©e par Angular (contexte : leur composant)Â
<div *ngIf="Angular"> Ok // </div>
<div *ngIf="AngularVersion > 4"> 🔥🔥 </div>
Nb : les 'template expressions' sont soumises à certaines limitations par rapport au javascript standard (chaînage d'expressions, etc)
Dir. structurelles : NgIf
Permet d'afficher un élément selon la valeur d'une expression booléenne.
<div *ngIf="false"></div>
<div *ngIf="a > b"></div>
<div *ngIf="str == 'yes'"></div>
<div *ngIf="myFunc()"></div>
Nb : appliquée à un composant, ngIf induit l'initialisation ou la destruction de celui-ci - cf la notion de 'cycle de vie' d'un composant.
Attention, NgIf fait apparaître ou disparaître l'élément auquel elle est attibuée du DOM - c'est donc par exemple différent d'un 'display: none'.
Dir. structurelles : NgSwitch
Fonctionne comme un 'switch case' traditionnel :
<div *ngIf="myVar == 'A'">Var is A</div>
<div *ngIf="myVar == 'B'">Var is B</div>
<div *ngIF="myVar == 'C'">Var is C</div>
<div *ngIf="myVar != 'A' && myVar != 'B' && myVar != 'C'"></div>
<div [ngSwitch]="myVar">
<div *ngSwitchCase="A">Var is A</div>
<div *ngSwitchCase="B">Var is B</div>
<div *ngSwitchCase="C">Var is C</div>
<div *ngSwitchDefault>Var is something else</div>
</div>
Ă©quivaut Ă :
Dir. structurelles : NgFor
Permet d'itérer sur un array :
import {Component} from '@angular/core'
@Component({
selector: 'todo-list',
template: `
<h2>Todos</h2>
<ul>
<li *ngFor="let todo of todos">{{todo}}</li>
</ul>
`
})
export class TodoList {
todos = ['Walk the dog', 'Stay in bed', 'Code more']
}
Directives d'attributs
Modifient le comportement ou l'apparence d'un élément.
Â
=> Doit avoir un sélecteur CSS
@Directive({ Â
selector: '[doNothing]' })
export class DoNothingDirective {
 constructor() { Â
console.log('Do nothing directive'); Â
}
}
Directives d'attributs
Les sélecteurs :
• un élément : footer.
• une classe (rare) : .alert.
• un attribut (le plus fréquent) : [color].
• un attribut avec une valeur : [color=red].
• une combinaison : footer[color=red].
Directives d'attributs
@Directive({ Â
selector: 'div.loggable[logText]:not([notLoggable=true])'
})
export class ComplexSelectorDirective {
 constructor() { Â
console.log('Complex selector directive'); Â
}
}
Un exemple de sélecteur :
Dir. d'attributs 'built-in" :
Angular nous fournit 3 directives d'attributs principale :
- ngStyle
- ngClass
- ngModel
Â
+ d'autres fournies par modules supplémentaires: FormsModule, RouterModule, Angular Material...
Dir. d'attributs - NgStyle
currentStyle: {};
setCurrentStyle() {
// Propriétés CSS
this.currentStyle = {
'color': this.color,
'fontSize': this.fontSize
};
}
Attribuer un objet de style CSS dynamique:
<!-- Avec ngStyle -->
<div [ngStyle]="currentStyle">Un style spécial !</div>
<!-- Traditionnel -->
<div [style.color]="color">Un style moins spécial</div>
Dir. d'attributs - NgClass
currentClasses: {};
setCurrentClasses() {
// valeurs booléennes attribuées à des noms de classes CSS
this.currentClasses = {
'saveable': this.canSave,
'modified': !this.isUnchanged,
'special': this.isSpecial
};
}
Attribuer des classes CSS par :
- string
- array
- objet
Dir. d'attributs - NgClass
<div [ngClass]="currentClasses">This div is initially saveable,
unchanged, and special</div>
<!-- toggle the "special" class on/off with a property -->
<div [class.special]="isSpecial">The class binding is special</div>
Objet attribué à ngClass :
Binding traditionnel :Â
Rmq NgStyle - NgClass
Modification dynamique de la valeur attribuée à la directive par modification d'une variable controlleur :
Rmq NgStyle - NgClass
Note :
=> recréer l'objet style en callback
Rmq NgStyle - NgClass
@Component({})
export class DynamicNgStyle {
this.color;
this.fontSize;
currentStyle: {};
setCurrentStyle() {
this.currentStyle = {
'color': this.color,
'font-size': this.fontSize
};
}
changeAStyle(color, fontSize) {
this.color = color;
this.fontSize = fontSize;
// Recréer l'objet style en callback
this.setCurrentStyle()
}
}
Rmq NgStyle - NgClass
@Component({})
export class DynamicNgStyle {
currentStyle = {}
currentStyle = {}
set color(value) {
this.currentStyle['color'] = value
}
set fontSize(value) {
this.currentStyle['font-size'] = value
}
changeAStyle(color, fontSize) {
this.color = color;
this.fontSize = fontSize;
console.log(this.currentStyle) // actualisé
}
}
(Additionnel) Getters & setters
Fonctions permettant de lier dynamiquement, dans un objet, une propriété A à une propriété B en exécutant un callback :
Â
- quand on accède à B = getter
- quand la valeur de A est modifiée = setter
(Additionnel) Getters & setters
@Component({})
export class testGetter {
prop1 = 1
get prop2() {
return this.prop1 + 3;
}
prop3 = this.prop1 + 3;
testProp() {
this.prop1 = 3;
console.log(this.prop2); // 6 (actualisée)
console.log(this.prop3); // 4 (non-actualisée)
}
}
(Additionnel) Getters & setters
@Component({})
export class testSetter {
set prop1(value) {
this.prop2 = value + 3
};
prop2: any;
prop3: any = this.prop1 + 3;
constructor() { this.prop1 = 1; }
testProp() {
this.prop1 = 3;
console.log(this.prop2); // 6 (actualisée)
console.log(this.prop3); // NaN
}
}
Dir. d'attributs - Custom
@Directive({ Â
selector: '[loggable]'
})
export class InputDecoratorOnSetterDirective {
 @Input('logText') Â
set text(value) { Â
console.log(value); Â
}
}
<div loggable logText="Hello">Hello</div>
// notre directive console.log "Hello"
On peut facilement crĂ©er nos propres directives d'attributs :Â
Directives & décorateurs
@HostListener('mouseover') onMouseOver() {
console.log('Host listener ...)
}
Quelques dĂ©corateurs utiles :Â
- @Input() : accéder aux valeurs des attributs (attribuées dans le template du composant parent)
- @HostListener() : accéder aux événements se produisant se l'élément hôte
- @HostBinding() : accĂ©der aux valeurs des propriĂ©tĂ©sÂ
Directives
TP:
Â
- Structurelles : implémenter un *ngIf (ex: toogle button) et un *ngFor
- Attribut : ngStyle et ngClass (exercice des couleurs)
- Custom : créer une animation au hover + au scroll (avancé)
Composants / Binding / Directives
TP de synthèse :
Base d'une application de display d'articles
Â
- Architecturer l'app + pose premiers composants
- Composant display à partir d'articles à partir d'un mock
- Interaction composants Ă partir d'events du DOM (avancĂ©) : rĂ©ception d'events Ă©coutĂ©es par un composant A dans un composant BÂ
Modules
Ambient IT - Academy 🤖
Romain Signes 🚀
import {NgModule} from '@angular/core'
import {CommonModule} from '@angular/common'
import {GreeterComponent} from './greeter.component'
@NgModule({
imports: [CommonModule],
declarations: [GreeterComponent],
exports: [GreeterComponent]
})
export class GreeterModule {
}
@NgModule
Nous allons reparler un instant de la classe NgModule :Â
@NgModule
NgModule nous permet -comme son nom l'indique- d'importer des modules Angular, et donc d'étendre largement les fonctionnalités de notre application (composants, directives, services, etc...).
Â
Deux types principaux de modules :Â
- les modules déjà installés dans le dossier Node par la CLI ('@angular/..')
- les modules Ă importer soi-mĂŞme (packages)
Attention, une fois installés, les modules doivent être importés par une instance NgModule.
@NgModule
Ne pas confondre modules Angular et modules javascript:
- Angular : seront importés et gérés par un NgModule
- Js: gérés par Webpack, ou autre module loader
@NgModule
Configuration de @ngModule :
import {CommonModule} from '@angular/common'
import {PackageModule} from 'package/module'
import {ServiceModule} from 'service/module'
import {MyComponent} from './my/my.component'
@NgModule({
// La plupart des modules importés seront déclarés ici
imports: [CommonModule,
PackageModule],
// Les composants que nous créons sont déclarés ici
declarations: [MyComponent],
// Lorsque les modules importés contiennent des service,
// ils sont déclarés ici - nous y reviendrons
providers: [GreeterComponent]
})
export class AppModule {}
Modules
TP:
Â
- Importer un module Angular (ex: Angular Material) et organiser les imports sous forme d'un feature Module
- Importer une librairie Javascript (ex: LocalForage)
Pipes
Ambient IT - Academy 🤖
Romain Signes 🚀
Introduction
Les pipes sont des opérateurs permettant d'appliquer une transformation à un input :
<p>{{ 10.6 | currency:'CAD':'symbol-narrow' }}</p>
<!-- retournera '$10.60' -->
Les pipes peuvent être utilisés :
- directement dans le template (ci-dessus)
- dans le composant => necessite l'import du pipe souhaité, ainsi que 'l'injection' du pipe via le constructeur (cf chapitre sur l'Injection de Dépendances)
Introduction
Utilisation dans un composant :
import { Component } from '@angular/core';
// Importer le pipe
import { CurrencyPipe } from '@angular/common';
@Component({
 selector: 'app-money',
 template: `<p>{{ stringAsCurrency }}</p>`
})
export class MoneyComponent {
 money: number = 1;
 stringAsCurrency: string;
 // injection du pipe
 constructor(currencyPipe: CurrencyPipe) {
 // appel sur le pipe de la méthode transform
 this.stringAsCurrency = currencyPipe.transform(this.money);
 }
}
Introduction
Paramétrisation des pipes :
<p>Une date: {{ myDate | date:"MM/dd/yy" }} </p>
Un pipe peut accepter des paramètres optionnels, pour affiner l'output.
=> {{ ... | nomDuPipe: paramètre1 : paramètre2 : ... }}
Introduction
Les paramètres passés aux pipes peuvent être des 'template expressions' :
<!-- Template -->
<p>Une date : {{ myDate | date:format }}</p>
<button (click)="changeFormat()">Changer le format</button>
// composant
export class ChangeDateFormatComponent {
myDate = new Date(2018, 1, 31); // 31 janvier 2018
changeFormat = true; // true == shortDate
get format() { return this.toggle ?
'shortDate' : 'fullDate'; }
changeFormat() { this.toggle = !this.toggle; }
}
Introduction
Enchaîner les pipes :
Une date enchaînée :
{{ myDate | date | uppercase}}
Une date enchaînée :
{{ myDate | date: format | uppercase}}
Pipes fournis : Â json
Applique simplement JSON.stringify()Â :
<p>{{ pizza | json }}</p>
// affichera :
<p>[ { "name": "Margarita" }, { "name": "Quatre fromages" } ]</p>
Nb: json est n'est pas très utilisé en production, mais bien pratique pour le débug.
Utilisation dans un template :
Utilisation dans un composant :
import { Component } from '@angular/core';
import { JsonPipe } from '@angular/common';
@Component({
selector: 'app-pizza',
template: `<p>{{ pizzaAsJson }}</p>`
})
export class PizzaComponent {
pizzas: Array<any> = [{ name: 'Margarita' }, { name: 'Quatre fromages' }];
pizzasAsJson: string;
constructor(jsonPipe: JsonPipe) {
this.pizzasAsJson = jsonPipe.transform(this.pizzas);
}
}
Nb: l'utilisation de pipes dans un composant présentant toujours une architecture similaire, nous ne donnerons en exemple que l'utilisation en template pour les pipes suivants.
Pipes fournis : Â slice
Applique slice() au sous-ensemble d’une collection (pour en afficher qu'une partie).
=> 2 paramètres : un indice de dĂ©part et, Ă©ventuellement, un indice de fin.Â
<!-- Sur un array -->
<p>{{ pizzas | slice:1:3 }}</p>
<!-- Sur une chaîne de caractères -->
<p>{{ 'Margarita' | slice:0:5 }}</p>
Pipes fournis : Â text format
Les pipes uppercase, lowercase et titlecase appliquent diffĂ©rentes transformations Ă des chaĂ®nes de caractères :Â
<p>{{ 'Quatre fromages' | uppercase }}</p>
<!-- affichera 'QUATRE FROMAGES' -->
<p>{{ 'Quatre fromages' | lowercase }}</p>
<!-- affichera 'quatre fromages' -->
<p>{{ 'Quatre fromages' | titlecase }}</p>
<!-- affichera 'Quatre Fromages' -->
Pipes fournis : Â number
<p>{{ 12345 }}</p>
<!-- affichera '12345' -->
<p>{{ 12345 | number }}</p>
<!-- affichera '12,345' -->
<p>{{ 12345 | number:'6.' }}</p>
<!-- affichera '012,345' -->
<p>{{ 12345 | number:'.2' }}</p>
<!-- affichera '12,345.00' -->
<p>{{ 12345.13 | number:'.1-1' }}</p>
<!-- affichera '12,345.1' -->
Pipes fournis : Â percent
<p>{{ 0.8 | percent }}</p>
<!-- affichera '80%' -->
<p>{{ 0.8 | percent:'.3' }}</p>
<!-- affichera '80.000%' -->
<p>{{ 10.6 | currency:'CAD' }}</p>
<!-- affichera 'CA$10.60' -->
<p>{{ 10.6 | currency:'CAD':'symbol-narrow' }}</p>
<!-- affichera '$10.60' -->
<p>{{ 10.6 | currency:'EUR':'code':'.3' }}</p>
<!-- affichera 'EUR10.600' -->
Pipes fournis : Â currency
Pipes fournis : Â date
<p>{{ myDate | date:'dd/MM/yyyy' }}</p>
<!-- affichera '16/07/1986' -->
<p>{{ myDate | date:'longDate' }}</p>
<!-- affichera 'July 16, 1986' -->
<p>{{ myDate | date:'HH:mm' }}</p>
<!-- affichera '15:30' -->
<p>{{ myDate | date:'shortTime' }}</p>
<!-- affichera '3:30 PM' -->
Nb: ce pipe est très similaire à la librairie Moment.js
Pipes fournis : Â async
Permet d’afficher des données obtenues de manière asynchrone en utilisant PromisePipe ou ObservablePipe.
=> Nous reviendrons sur ce pipe dans le chapitre concernant les Observables dans RxJS.
Â
Un pipe async retourne une chaîne de caractères vide jusqu’à ce que les données deviennent disponibles (ex: promise résolue, dans le cas d’une promise), puis :
- retourne la valeur obtenue
- déclenche un cycle de détection de changement une fois la donnée obtenue (cf Observables)
Pipes fournis : Â async
import { Component } from '@angular/core';
@Component({
 selector: 'ns-greeting',
 template: `<div>{{ asyncGreeting | async }}</div>`
})
export class GreetingComponent {
 asyncGreeting = new Promise(resolve => {
 // promise resolve après 1 seconde
 window.setTimeout(() => resolve('hello'), 1000);
 });
}
Exemple utilisant une promesse :
Custom pipes
Pour créer un custom pipe, il suffit de :
- créer une nouvelle classe
- y ajouter le décorateur @Pipe({name: "..", ..})
- y implémenter l’interface PipeTransform, ce qui nous amène à écrire une méthode tranform()
import { PipeTransform, Pipe } from '@angular/core';
@Pipe({ name: 'newPipe' })
export class NewPipe implements PipeTransform {
transform(value, args) {
return ...;
}
}
Custom pipes
Il faut ensuite rendre ce pipe disponible dans l'application en le déclarant dans ngModule :
@NgModule({
 imports: [..],
 declarations: [..., NewPipe],
 bootstrap: [..]
})
export class AppModule
Custom pipes - exemple
Pipe affichant le temps écoulé depuis une date à l'aide de la librairie Moment.js :
=> Nous utiliserons la function fromNow() de Moment.js
npm install moment
1. Â installer Moment.js avec NPM :
Nb: Les types nécessaires pour TypeScript sont déjà inclus dans la dépendance NPM, nous pouvons donc déjà profiter d'un typage prédéfini.
Custom pipes - exemple
import { PipeTransform, Pipe } from '@angular/core';
import * as moment from 'moment';
export class FromNowPipe implements PipeTransform {
 transform(value, args) {
 return moment(value).fromNow();
 }
}
2.  CrĂ©er le pipe :Â
3.  Le rendre disponible (en le déclarant dans ngModule)
Pipes
TP:
Â
- Utiliser un Pipe fourni
- Pipe Async avec une Promesse
- Custom Pipe (ex: limiter le nombre de caractères d'un texte)
Services
Ambient IT - Academy 🤖
Romain Signes 🚀
Injection de dépendances
DĂ©pendance :
- un composant C consomme une fonction F
- F est déclarée dans un service S
C dépend de S => S est une dépendance de C
Â
2 possibilités :
- C créé une instance de S
- le framework créé une instance de S, qu'il 'injecte' dans C
= injection de dépendance
Â
Exemple
Une dépendance est simplement une classe que l'on va 'injecter' dans une autre classe (généralement service --> composant) :
export class ApiService { Â
get(path) { Â
// todo: appeler le backend Â
}
}
Exemple
Signaler l'injection : le dĂ©corateur @InjectableÂ
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http'
@Injectable()
export class ApiService {
get(path) {
// todo: appeler le backend
// en utilisant la dépendance Http
}
}
Injection de dépendances
Pour injecter notre dépendance, on a ensuite besoin :
Â
• d’une façon d’enregistrer la dépendance, pour la rendre disponible à l’injection dans d’autres composants/services.
Â
• d’une façon de la déclarer dans nos composants ou services.
Injection de dépendances
Enregistrer une dépendance :
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class TestService {
constructor() { }
}
Injection de dépendances
Déclarer la dépendance :
...
import { ApiService } from './../api-service';
@Component({ ... })
export class Component {
constructor( public apiService : ApiService ) {}
useService() {
this.apiService.....subscribe(
data => // use data
)
}
}
Les services
Les services correspondent à la façon la plus utilitaire d'injecter des dépendances.
Â
Principe:
Quand plusieurs composants ont besoin de faire la mĂŞme chose :
- factoriser le code correspondant dans un service
- injecter dans les composants
Les services
Créer un service :
import {Injectable} from '@angular/core';
@Injectable()
export class LoginService {
constructor() {}
doSomething() {}
}
Les services
Injecter un service :
import {Component} from '@angular/core';
import {LoginService} from 'login.service';
@Component({
selector: 'my-component',
providers: [LoginService],
template: require('./my.component.html')
})
export class MyComponent {
constructor(private loginService: LoginService) { }
ngOnInit() {
this.loginService.doSomething();
}
}
Routage
Ambient IT - Academy 🤖
Romain Signes 🚀
Introduction
But: associer une URL à un état de l’application (meilleure UX)
Â
=> routeur : chaque framework a le sien
Â
En Angular, il s'agit du module RouterModule.
RouterModule
Nb: optionnel => Ă inclure dans ngModule
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule } from '@angular/router';
// Configuration, cf prochaine slide
import { ROUTES } from './app.routes';
...
@NgModule({
 imports: [BrowserModule, RouterModule.forRoot(ROUTES)],
 declarations: [..],
 bootstrap: [..]
})
export class AppModule {
}
RouterModule
Configuration du module :
Nb: cela peut se faire dans un fichier dédié, généralement nommé app.routes.ts.
import { Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { OtherComponent } from './other/other.component';
export const ROUTES: Routes = [
 { path: '', component: HomeComponent },
 { path: 'other', component: OtherComponent }
]
RouterModule
Inclure le composant 'routé' dans le template :
<router-outlet></router-outlet>.
RouterModule
Exemple :
<header>
<nav>...</nav>
</header>
<main>
<router-outlet></router-outlet>
<!-- le template du composant 'routé' sera
inclu ici -->
</main>
<footer>fait avec <3 par ...</footer>
Navigation :
Naviguer entre différents composants ?
Â
En effet, avec des liens "classiques" :
click --> reload page --> relance toute l'app (SPA)
Â
=> utiliser une directive particulière : routerLink.
RouterLink :
RouterLink - argument :
    - le chemin (string)
    - le chemin + paramètres (array<string>)
Â
Nb: importer cette directive ?
RouterLink :
Exemples :
<a href="" routerLink="/">Home</a>
<!-- idem -->
<a href="" [routerLink]="['/']">Home</a>
RouterLinkActive - ajouter une classe CSS lorsque le lien pointe sur la route courante :
<a href="" routerLink="/" routerLinkActive="selected-menu">
Home</a>
navigate() :
Naviguer depuis le composant :Â
- injecter le service Router (cf partie sur la DI)
- utiliser sa méthode navigate()
export class navigationComponent {
// Injection d'une instance du Router
 constructor(private router: Router) {
 }
 saveAndMoveBackToHome() {
 // Route :
 this.router.navigate(['']);
 }
}
Urls dynamiques :
-  définir une route dans la configuration avec des paramètres dynamiques ( " ../:paramDyn/.. ")
export const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'newsFeed', component: newsFeedComponent },
{ path: 'profiles/:profileId', component: ProfileComponent }
];
- définir des liens dynamiques ("[routerLink] = "['paramStatic', paramDyn, ...]"
<a href="" [routerLink]="['/profiles', profile.id]">
Voir profil</a>
rxjs
Ambient IT - Academy 🤖
Romain Signes 🚀
Design pattern
Observable :
Fonction qui associe une source de données à un observeur + retourne un moyen d'annuler cette liaison.
Â
Observeur:
Objet ayant une méthode next(), et optionnellement complete() et error().
Design pattern
function monObservable(observer) {
let array = [1, 2 , 3 , 4]
array.forEach(
(el) => observer.next(el)
)
observer.complete()
}
let observer = {
next : (value) => console.log('Nouvelle valeur : ' + value),
complete : () => console.log('Terminé.')
}
La librairie Rxjs
Rxjs permet de crĂ©er des observables :Â
- "safe"
- grâce à des outils
const my_observable = new Observable(
(observable) => observable.next(42))
const my_observable2 = Observable.of(42);
of est la méthode la plus simple et permet de créer un observable n'envoyant qu'une seule valeur.
Rxjs - Subscribe
Méthode subscribe() permet d'exécuter l'observable :
const my_observable = Observable.of(42);
my_observable.subscribe(
(value) => console.log(value),
(error) => console.log(error),
() => console.log('terminé')
);
my_observable.complete();
Rxjs - Pipe
MĂ©thode pipe() (Angular 6+) permet de transformer l'output, en revoyant un nouvel observable:Â
const my_observable = Observable.of(42);
my_observable
.pipe(
map( val => val - 10))
.subscribe(
(value) => console.log(value));
Un cas concret
Angular nous propose justement de nombreux services exploitant à fond la programmation réactive, tel que HTTP.
Les différentes méthodes du service http retournent des Observable<Response>. Notez que le type des variables émises par l'observable est précisé entre chevrons, les observables sont en effet génériques.
http
Ambient IT - Academy 🤖
Romain Signes 🚀
Le module HttpClient
Attention :
On utilise le nouveau HttpClientModule introduit avec Angular 4.3 dans le package @angular/common/http, qui est une réécriture complète du HttpModule qui existait jusqu’à alors. Ce chapitre ne parle pas de l’ancien HttpModule du package @angular/http qui était utilisé précédemment.
Le module HttpClient
Nb :
Traditionnellement, on utilise HTTP, mais il y a des alternatives :
- WebSockets
- bibliothèques HTTP, comme l’API fetch, pour le moment disponible sous forme de polyfill, mais qui devrait devenir standard dans les navigateurs.
HttpClient - implémentation
L'implémentation du module est assez simple :
1. Le déclarer dans app.module.ts
2. "L'injecter" partout oĂą on en a besoin
@Component({ Â
selector: 'xxx', Â
template: `<h1>xxx</h1>`
})
export class RacesComponent {
 constructor(private http: HttpClient) {  }
}
HttpClient - implémentation
Il propose plusieurs méthodes, correspondant au verbes HTTP communs :
get • post • put • delete • patch • head • jsonp
Â
Une requĂŞte est effectuĂ©e de la façon suivante :Â
http.get(`${baseUrl}/api/foo/bar`)
Cela retourne un Observable => on doit donc s'y abonner pour obtenir la rĂ©ponse.Â
Â
HttpClient - implémentation
Abonnement :Â
http.get(`${baseUrl}/api/foo/bar`)
.subscribe((response: Foobar) => { console.log(response)});
Nb : Le corps de la rĂ©ponse, qui est la partie la plus intĂ©ressante, est directement Ă©mis par l’Observable. On peut nĂ©anmoins accĂ©der Ă la rĂ©ponse HTTP complète : Â
http.get(`${baseUrl}/api/foo/bar`, { observe: 'response' }) Â
.subscribe((response: HttpResponse<Foobar>) => { Â
console.log(response.status); // logs 200 Â
console.log(response.headers.keys()); // logs [] Â
});
HttpClient - implémentation
Envoyer des données est aussi trivial. Il suffit d’appeler la méthode post(), avec l’URL et l’objet à poster :
Â
http.post(`${baseUrl}/api/foo/bar`, FooBar)
.subscribe((response: Foobar) => { console.log(response)});
Formulaires
Ambient IT - Academy 🤖
Romain Signes 🚀
Les formulaires en AngularÂ
Angular propose 2 types de formulaires:
 - "template driven": formulaires simples, peu de validation.
 - "reactive forms": représentation du formulaire dans le contrôleur
=> plus verbeux, mais aussi plus puissant (validation custom)
Les formulaires en AngularÂ
Fonctionnement :
Dans les 2 cas, Angular crée une représentation de notre champ sous la forme d'un arbre d'objets de la classe FormControl.
Â
- "template driven" : objet créé en interne
- "reactive form" : objet créé manuellement dans le composant
Template driven
<h2>Sign up</h2>
<form (ngSubmit)="register()">
 <div>
 <label>Username</label>
<input name="username" ngModel>
 </div> Â
<div>
 <label>Password</label>
<input type="password" name="password" ngModel>
 </div>
 <button type="submit">Register</button>
</form>
Il suffit d'ajouter les directives ngModel, qui crĂ©e le FormControl, et le <form> crĂ©e automatiquement le FormGroup.Â
Reactive forms :Â
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, FormControl } from '@angular/forms';
@Component({
selector: 'ns-register',
templateUrl: 'register-form.component.html', })
export class RegisterFormComponent {
usernameCtrl: FormControl;
passwordCtrl: FormControl;
userForm: FormGroup;
constructor(fb: FormBuilder) {
this.usernameCtrl = fb.control('');
this.passwordCtrl = fb.control('');
this.userForm = fb.group({
username: this.usernameCtrl,
password: this.passwordCtrl
});
}
 reset() {
 this.usernameCtrl.setValue('');
 this.passwordCtrl.setValue(''); Â
}
 register() {
 console.log(this.userForm.value); Â
}
}
On créé 'manuellement' le formulaire :
Reactive forms :Â
<h2>Sign up</h2>
<form (ngSubmit)="register()" [formGroup]="userForm">
 <div>
 <label>Username</label>
<input formControlName="username">
</div>
<div>
 <label>Password</label>
<input type="password" formControlName="password">
 </div> Â
<button type="submit">Register</button>
</form>
Que l'on lie ensuite au template :Â
Du styleÂ
 input.ng-invalid {
 border: 3px red solid;
 }
=> ajout / retrait automatique classes CSS selon l'Ă©tat du formula
Â
Exemple: ng-invalid si un de ses validateurs Ă©choue
AttributsÂ
Un FormControl a plusieurs attributs :
• valid : si le champ est valide, au regard des contraintes et des validations qui lui sont appliquées.
• invalid : si le champ est invalide, au regard des contraintes et des validations qui lui sont appliquées.
• errors : un objet contenant les erreurs du champ.
etc....
Â
+ quelques méthodes comme hasError() pour savoir si le contrôle a une erreur donnée.
Les formulaires en AngularÂ
const password = new FormControl('abc');
console.log(password.dirty);
console.log(password.value);
console.log(password.hasError('required'));
On peut donc Ă©crire :
Ces contrôles peuvent être regroupés dans un FormGroup ("groupe de formulaire") pour constituer une partie du formulaire qui a des règles de validation communes. Un formulaire lui-même est un groupe de contrôle.
Les formulaires en AngularÂ
const form = new FormGroup({
 username: new FormControl('Cédric'),
 password: new FormControl() });
console.log(form.dirty);
Un FormGroup a les mêmes propriétés qu’un FormControl, avec quelques différences :
• valid : si tous les champs sont valides, alors le groupe est valide.
• invalid : si l’un des champs est invalide, alors le groupe est invalide.
• Etc ...
Validation
 constructor(fb: FormBuilder) {
 this.userForm = fb.group({
 username: fb.control('', [Validators.required,
Validators.minLength(3)]),
 password: fb.control('', Validators.required)
 }); Â
Reactive form :Â
Angular nous permet de rajouter des paramètres de validation faicilement.Â
Validation
<h2>Sign up</h2>
<form (ngSubmit)="register(userForm.value)"
#userForm="ngForm">
<div>
<label>Username</label><input name="username"
ngModel required minlength="3">
</div>
<div>
<label>Password</label><input type="password"
name="password" ngModel required>
</div>
<button type="submit">Register</button>
</form>
Template driven :Â
Validation
Quelques validateurs sont fournis par le framework :
• Validators.required pour vérifier qu’un contrôle
n’est pas vide ;
• Validators.minLength(n) pour s’assurer que la valeur
entrée a au moins n caractères ;
• Validators.maxLength(n) pour s’assurer que la valeur
entrée a au plus n caractères ;
• Validators.email() (disponible depuis la version 4.0)
pour s’assurer que la valeur entrée est une adresse email valide
• Validators.min(n) (disponible depuis la version 4.2)
pour s’assurer que la valeur entrée vaut au moins n ;
• Validators.max(n) (disponible depuis la version 4.2)
pour s’assurer que la valeur entrée vaut au plus n ;
• Validators.pattern(p) pour s’assurer que la valeur
entrée correspond à l’expression régulière p définie.
Erreurs et soumissionÂ
<h2>Sign up</h2>
<form (ngSubmit)="register()" [formGroup]="userForm">
 <div>
 <label>Username</label>
<input formControlName="username">
 </div>
 <div>
 <label>Password</label>
<input type="password" formControlName="password">
 </div>
 <button type="submit" [disabled]="userForm.invalid">
Register
</button>
</form>
Reactive form:
Copy of Angular
By AdapTeach
Copy of Angular
- 848