100% ont trouvé ce document utile (1 vote)
1K vues1 016 pages

Angular

Transféré par

il
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
100% ont trouvé ce document utile (1 vote)
1K vues1 016 pages

Angular

Transféré par

il
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Angular

Introduction
AYMEN SELLAOUTI

1
Références

2
C’est quoi NodeJs?

3
C’est quoi NodeJs?
[Link]® is a JavaScript runtime built on Chrome's V8 JavaScript engine.
[Link] uses an event-driven, non-blocking I/O model that makes it
lightweight and efficient. [Link]' package ecosystem, npm, is the
largest ecosystem of open source libraries in the world.

Environnement d’exécution JS

Utilise V8 le moteur JS de Google.

4
Moteur Javascript : Javascript Engine
 Tout code JavaScript que vous
écrivez a besoin d’un moteur
Javascript pour l’exécuter.
 Le moteur JavaScript possède
deux composants principaux:
Memory Heap : sert à allouer la
mémoire utilisée dans le
programme.
Call Stack (pile d’exécution) :
contient les données des fonctions
exécutées par le programme.
5
Moteur Javascript : Javascript Engine

6
V8 Javascript Engine
 V8 est un moteur JavaScript développé par Google et utilisé par
chrome pour exécuter du code JavaScript.
 Il est Open Source
 Ecrit en C++

[Link] 7
Qu’est ce qu’un environnement
d’exécution JS
 Un environnement d’exécution JS est l’endroit ou votre code JS va
être exécuté. C’est la ou vivra votre JavaScript Engine.
 Il va, entre autre,. déterminer quels variables globales sont accessibles
pour vous (window pour le runtime envirement de votre browser)
 Ajax, DOM et d’autres API’s ne font pas parti de JavaScript. C’est
l’environnement d’exécution Js (Ici fourni par votre Browser) qui les rend
disponible.

[Link] 8
Est-ce que JavaScript est Synchrone
ou Asynchrone ?
 Js est SYNCHRONE.
 Les fonctions asynchrones telles que setTimout ne font pas partie
de JS.
 Elle font partie des API de votre runtime envirement.
 Donc JS peut agir d’une façon ASYNCHRONE mais ceci n’est
pas innée, ce n’est pas dans son Core mais plutôt du au API offert par
le runtime.
Dans NodeJs c’est libuv qui permet cet aspet Asynchrone.

[Link] 9
Asynchrone
 Un code asynchrone est un code non bloquant
 Une fonction non bloquante est une fonction qui s’exécute en
parallèle avec le reste du code.

Exemple : setTimeout, makeRequest, readFile.

[Link] 10
Qu’est ce qu’un environnement
d’exécution JS
 Si on gardait uniquement l’aspect
Synchrone de JS, ca sera un
problème énorme avec le browser.
La solution proposée par les API
est l’ajout e la composante
synchrone via l’event loop et le
callback queue.
 Ceci va permettre d’effectuer des
traitements sans bloquer le stack
du moteur JS.
[Link] 11
Comment ca marche dans le browser ???
A part le moteur JS, votre
environnement possède des API’s
qui permettent à votre Moteur JS
d’exécuter certaines
fonctionnalités comme le
timeout, ou fetch.
A ces API’s est associé un event
loop et des callback queue.
 Pour le browser vous avez l’API
DOM.
[Link] 12
Comment est né Node ?
 Au départ on ne pouvait exécuter Js que dans un Browser. C’est lui qui fournissait
son environnement d’exécution.

 Une question a été donc posée. Pouvons nous exécuter du Js en dehors du


browser ? Pouvons nous avoir du Js côté Serveur ?

13
Comment est né Node ?

2008

2009

14
Comment est né Node ?

[Link] 15
NodeJs un environnement d’exécution
 Afin de s’exécuter, Javascript a besoin d’un environnement
d’exécution.
 C’est comme un conteneur qui contient tout ce qu’il vous faut pour
exécuter du code Js.
 Le noyau de l’environnement d’exécution est le moteur Js.
 Il y a aussi les APIs Node
 Libuv la bibliothèque qui permet de gérer les I/O Asynchrone
 Les nodes Js Binding

16
NodeJs un environnement d’exécution
Moteur JavaScript NodeJs APIs

Bindings
NodeJs
Fs http

path crypto

17
Asynchrone
 Un code asynchrone est un code non bloquant.
 Dans sa présentation, NodeJs se présente comme étant un
environnement d’exécution JavaScript asynchrone et dirigé par les
événements.
Imaginez qu’on veuille lire deux fichiers indépendants.
« Time Line » du Modèle « bloquant »

« Time Line » du Modèle « Non-bloquant »

18

18 1
[Link] 8
Est-ce que NodeJs est Multi thread ?
 NodeJs est monothread au départ puis le multithrading a été introduit.
 Vous avez encore V8 comme Moteur Js.
 La gestion de l’aspect Asynchrone est offerte par la bibliothèque libuv

[Link] 19
libuv
 libuv est une bibliothèque multiplatforme écrite en C
 Son rôle est de gérer les opération asynchrone d’I/O
 Elle permet à plusieurs langages de s’y connecter via les connecteurs
de bindings.
Elle offre la partie principale qui gérer les traitements Asynchrone de
NodeJs, l’EVENT LOOP.

[Link] 20
libuv

Thread Pool Asynchrone I/O

Gestion de fichiers

Réseau

Event Loop

[Link] 21
libuv
Processus Thread Thread 1 Thread 2 Thread 3 Thread 4
Node Principal

Pile Pile Pile Pile Pile


Code d’exécution d’exécution d’exécution d’exécution d’exécution

[Link] 22
libuv
 libuv offre un thread pool.
 Le nombre de thread est par défaut de 4 et il est extensible jusqu’à 128
 Afin de le modifier, il faut utiliser la variable d’environnement
UV_THRADPOOL_SIZE
 Le thread pool ne gère pas automatiquement toutes les
opérations asynchrones.
 L’idée de base est d’éviter ca et de déléguer ca à l’OS tant qu’il peut le
faire.

[Link] 23
libuv
 Le pool gère donc les opérations d'E/S asynchrones pour lesquelles le
noyau de votre OS a une prise en charge faible
 Ceci s’applique pour les opérations de système de fichiers (fs) et les
opérations DNS (dns)
 Il est également utilisé pour le travail asynchrone lié au processeur
dans les modules principaux de [Link], à savoir la compression (zlib) et
la cryptographie (crypto).

[Link] 24
Libuv
event loop
Stack Node Thread Pool
API OS
[Link]("hello node");
setTimeout(() => {
[Link]("second Hello");
}, 3000);
[Link]("kepler_data.csv") Timer Task Queue
.on("data", (data) => {
[Link]("file data: ", data)); I/O Task Queue
}
.on("end", () => [Link]("end")) Immediate Task Queue
.on("error", (error) => { Event Loop
[Link]("error : ", error); Close Task Queue
});
[Link]("bye node"); [Link] 25
Libuv
event loop

[Link]('cc node');
setTimeout(function () {
for (let i=1; i< 100000; i++) {
[Link]('procssing',i);
}
[Link]('end processing :D');
},0);

[Link]('bye node');

26
Libuv
event loop
 Afin de bien travailler avec l’event loop éviter :
 Le calcul complexe et couteux en CPU
 Les objets JSON lourd
 Le expressions régulières complexes.

[Link] 27
C’est quoi Angular?

28
C’est quoi Angular?
Framework JS

SPA

Supporte plusieurs langages : ES5, EC6, TypeScript, Dart

Rapide

Orienté Composant

29
Est-ce qu’Angular est stable ?????

30
SPA
Application Web
C
traditionnelle H
T J
S
M
L S S
H
C
T J
M S
L
S S
H
C
T J
M S
L
S S
C
S
C
S JS S
H S
T H JS
M T
M
L SPA L
API 31
Angular : Arbre de composants

32
Architecture Angular

33
Principaux concepts et notions
Component Template DataBinding

Méta données

Injection de
Service Route
dépendance

34
Les librairies d’Angular
 Ensemble de modules JS

Des librairies qui contiennent un ensemble de fonctionnalités.

Toutes les librairies d’Angular sont préfixées par @angular

Récupérable à travers un import JavaScript.

Exemple pour récupérer l’annotation component : import { Component } from '@angular/core';

35
Les composants
 Le composant est la partie principale d’Angular.

Un composant s’occupe d’une partie de la vue.

L’interaction entre le composant et la vue se fait à travers une API.

36
Template

Un Template est le complément du composant.

C’est la vue associée au composant.

Elle représente le code HTMl géré par le composant.

37
Les méta data

 Appelé aussi « decorator », ce sont des informations permettant de


décrire les classes.

 @Component permet d’identifier la classe comme étant un


composant angular.

38
Le Data Binding
 Le data binding est le mécanisme qui permet de mapper
des éléments du DOM avec des propriétés et des méthodes
du composant.

Le Data Binding permettra aussi de faire communiquer


les composants.

39
Les directives
 Lesdirectives Angular sont des classes avec la métadata @Directive. Elle
permettent de modifier le DOM et de rendre les Template dynamiques.

Apparaissent dans des éléments HTML comme les attributs.

Un composant est une directive à laquelle Angular a associé un Template.

Ils existe deux autres types de directives :


Directives structurelles

Directive d’attributs

40
Les services
 Classes permettant d’encapsuler des traitements métiers.

Doivent être légers.

Associées aux composants et autres classes par injection de dépendances.

41
Installation d’Angular
Deux méthodes pour installer un projet Angular.

 Cloner ou télécharger le QuickStart seed proposé par Angular.

Utiliser le Angular-cli afin d’installer un nouveau projet (conseillé).

Remarque : L’installation de NodeJs est obligatoire afin de pouvoir


utiliser son npm (Node Package Manager).

42
Installation d’Angular
QuickStart
Deux méthodes
 Télécharger directement le projet du dépôt Git
[Link]
 Ou bien le cloner à l’aide de la commande suivante :
git clone [Link] quickstart
 Se positionner sur le projet
Installer les dépendance à l’aide de npm : npm install
 lancer le projet à l’aide de npm : npm start

43
Installation d’Angular
Angular Cli
 Nous allons installer notre première application en utilisant angular Cli.
 Si vous avez Node c’est bon, sinon, installer NodeJs sur votre machine. Vous devez avoir
une version de node >= 14.2
 Une fois installé vous disposez de npm qui est le Node Package Manager. Afin de vérifier
si vous avez NodeJs installé, tapez npm –v.
 Installer maintenant le Cli en tapant la : npm install -g @angular/cli
 npm install -g @angular/cli@16.2.15 installe la version 16.2.15
 npm view @angular/cli affiche la liste des versions de la cli
 Installer un nouveau projet à l’aide de la commande ng new nomProjet
 npx @angular/cli@17.3.9 new nomProjet
 Afin d’avoir du help pour le cli tapez ng help
 Lancer le projet en utilisant la commande ng serve

44
Angular versions

45
Installation d’Angular
Angular Cli
Positionnez vous maintenant dans le dossier
Tapez la commande suivante : ng new nomNewProject
 lancez le projet à l’aide de npm : ng serve
Naviguez vers l’adresse mentionnée.
 Vous pouvez configurer le Host ainsi que le port avec la commande
suivante : ng serve --host leHost --port lePort

Pour plus de détails sur le cli visitez [Link]

46
Quelques commandes du Cli
Commande Utilisation
Component ng g component my-new-component
Directive ng g directive my-new-directive
Pipe ng g pipe my-new-pipe
Service ng g service my-new-service
Class ng g class my-new-class
Interface ng g interface my-new-interface
Module ng g module my-module

47
Arborescence d’un projet Angular

Application modulaire Application moduless

48
Ajouter Bootstrap
On peut ajouter Bootstrap de plusieurs façons :
1- Via le CDN à ajouter dans le fichier [Link]
2- En le téléchargeant du site officiel
3- Via npm
◦ Npm install bootstrap --save

49
Ajouter Bootstrap
Pour ajouter les dossiers téléchargés on peut le faire de deux façons :
1- En l’ajoutant dans [Link]
2- En ajoutant le chemin des dépendances dans les tableaux styles et scripts dans le fichier
[Link]:
"styles": [
"../node_modules/bootstrap/dist/css/[Link]",
"[Link]",
],
"scripts": [
"../node_modules/bootstrap/dist/js/[Link]"
],

50
Ajouter Bootstrap
Ajouter dans le fichier src/[Link] un import de vos bibliothèques.

@import "./../node_modules/bootstrap/";

Essayer la même chose avec font-awesome.

51
Angular
Les composants
AYMEN SELLAOUTI

52
Objectifs
1. Comprendre la définition du composant

2. Assimiler et pratiquer la notion de Binding

3. Gérer les interactions entre composants.

53
Qu’est ce qu’un composant (Component)
 Un composant est une classe qui permet de gérer une vue. Il se charge uniquement
de cette vue la.
Plus simplement, un composant est un fragment HTML géré par une classe JS
(component en angular et controller en angularJS)
 Une application Angular est un arbre de composants
 La racine de cet arbre est l’application lancée par le navigateur au lancement.
 Un composant est :
 Composable (normal c’est un composant)
 Réutilisable
 Hiérarchique (n’oublier pas c’est un arbre)
NB : Dans le reste du cours les mots composant et component représentent
toujours un composant Angular.
54
Angular : Arbre de composants

55
Quelques exemples

56
Quelques exemples

57
Quelques exemples

58
Quelques exemples

59
Quelques exemples

60
Quelques exemples

61
Premier Composant
Le décorateur @Component permet d’ajouter un
@Component({
comportement à notre classe et de spécifier que c’est un
selector: "app-root", Composant Angular.
standalone: true, selector permet de spécifier le tag (nom de la balise)
imports: [RouterOutlet, RouterLink], associé ce composant
templateUrl: "./[Link]", standalone permet de spécifier si le composant est
standalone ou pas
styleUrl: "./[Link]",
imports L’ensemble des dépendances du composant
}) templateUrl: spécifie l’url du template associé au
export class AppComponent { composant
title = "ng18"; styleUrls: tableau des feuilles de styles associé à ce
} composant

62
Création d’un composant
 Deux méthodes pour créer un composant
Manuelle
Avec le Cli
 Cli, Avec la commande ng generate component my-new-component ou son raccourci
ng g c my-new-component

63
Création d’un composant
 La commande generate possède plusieurs options
OPTION DESCRIPTION

--inlineStyle=true|false Inclus les styles css dans le composant


Aliases: -s
--inlineTemplate=true|false Inclus le template dans le composant
Aliases: -t
--prefix=prefix Le préfixe à appliquer pour la génération des
composants
Valeur par défaut: app
Aliases: -p

[Link] 64
Property Binding
Balises Attributs
Evènement
Attributs Méthodes
HTML TS

65
Property Binding
@Component({
selector: 'app-color',
templateUrl: './[Link]',
styleUrls: ['./[Link]'],
<div [[Link]]="color"> providers: [PremierService]
Color })
</div> export class ColorComponent implements OnInit {
color = 'red';
<input [(ngModel)]="color" constructor() { }
type="text"
class="form-control" ngOnInit() {}
> processReq(message: any) {
le contenu de la propriété color est {{color}} alert(message);
<button (click)="loggerMesData()">log data</button> }
<br> loggerMesData() {
<a (click)="goToCv()" >Go to Cv</a> [Link]('test');
}
HTML goToCv() {
const link = ['cv'];
[Link](link);
}
TS
66
Property Binding
 Binding unidirectionnel.
 Permet aussi de récupérer dans le DOM des propriétés du composant.
 La propriété liée au composant est interprétée avant d’être ajoutée au
Template.
 Deux possibilités pour la syntaxe:
 [propriété], exemple [hidden]=false
 bind-propriété, exemple bind-hidden=false

67
Event Binding
 Binding unidirectionnel.
 Permet d’interagir du DOM vers le composant.
L’interaction se fait à travers les événements.
Deux possibilités pour la syntaxe :
 (evenement), exemple (click)=changeBehavior()
 on-evenement

68
Property Binding et Event Binding
import { Component } from '@angular/core';

@Component({
<hr>
selector: 'inter-interpolation', Nom : {{nom}}<br>
template : `[Link]` , Age : {{age}}<br>
styles: [] Adresse : {{adresse}}<br>
})
export class InterpolationComponent { //Property Binding
nom:string ='Aymen Sellaouti'; <input #name
age:number =35; [value]="getName()">
adresse:string ='Chez moi ou autre part :)';
getName(){
//Event Binding
return [Link]; <button
} (click)="modifier([Link])"
modifier(newName){ >Modifier le nom</button>
[Link]=newName;
} <hr>
}

Component Template

69
Exercice
 Créer un nouveau composant. Ajouter y un Div et un input de type texte.
 Fait en sorte que lorsqu’on écrive une couleur dans l’input, ca devienne la couleur
du Div.
 Ajouter un bouton. Au click sur le bouton, il faudra que le Div reprenne sa couleur
par défaut.
 Ps : pour accéder au contenu d’un élément du Dom utiliser #nom dans la balise et
accéder ensuite à cette propriété via le nom.
 Pour accéder à une propriété de style d’un élément on peut binder la propriété
[[Link]été]
exemple
[[Link]]

70
Two way Binding
 Binding Bi-directionnel
 Permet d’interagir du Dom ver le composant et du composant vers le
DOM.
Se fait à travers la directive ngModel ( on reviendra sur le concept de
directive plus en détail)
Syntaxe :
[(ngModel)]=property
Afin de pouvoir utiliser ngModel vous devez importer le FormsModule dans
[Link]

71
Property Binding et Event Binding
import { Component} from
'@angular/core';
<hr>
@Component({ Change me if u can
selector: 'app-two-way', <input
templateUrl: './two-
[(ngModel)]="two">
[Link]',
styleUrls: ['./two- <br>
[Link]'] it's always me :d
}) {{two}}
export class TwoWayComponent {
two:any="myInitValue";
}
Component Template

72
Exercice
Le but de cet exercice est de créer un aperçu de carte visite.
Créer un composant
Préparer une interface permettant de saisir d’un coté les données à
insérer dans une carte visite. De l’autre coté et instantanément les
données de la carte seront mis à jour.
Préparer l’affichage de la carte visite. Vous pouvez utiliser ce thème
gratuit :

[Link]

73
Exercice

74
Exercice

75
Résumé : Property Binding

76
Angular 17
Le nouveau flux de control
Afin de continuer sur la logique de simplification de l’apprentissage
d’Angular, l’équipe Angular a proposé de nouveaux flux de
contrôle.
 Les flux suivants ont été proposés :
 @If
 @for
 @switch

77
Control flow
@if
@if est associé à une expression booléenne. Si cette expression est
fausse (false) alors l'élément et son contenu sont retirés du DOM, ou
jamais ajoutés).
@if(condition)
Si le booléen est true alors l’élément host est visible.
Si le booléen est false alors l’élément host est caché.
@if (isAuthenticated()) {
<button
(click)="deleteCv(cv)"
class="btn btn-danger">
Delete
</button>
}
78
Control flow
@if, @else if et @else
@if peut également être utilisé avec @else if et/ou @else selon le
besoin.
La syntaxe est très intuitive, demandé vous comment vous aurez fait
en Javasript et ajoutez un @ :D.

@if (connectedUser) {
Hello {{ [Link]}}
} @else {
<div>Merci de vous connectez</div>
}

79
Control flow
@if, @else if et @else
@if (!connectedUser) {
<div class="alert alert-danger">Merci de vous connectez</div>
} @else if (![Link]) {
<div class="alert alert-warning">
Hello {{ [Link] }}, merci d'activer votre compte
</div>
} @else {
<div class="alert alert-success">Hello {{ [Link] }}</div>
}

80
8

Control flow
1

@For
La directive structurelle @for permet de boucler sur un itérable et
d'injecter les éléments dans le DOM.

<ul>
<li *ngFor="let episode of episodes">{{ [Link] }}</li>
</ul>
<ul>
@for (episode of episodes; track [Link]) {
<li>{{ [Link] }}</li>
}
</ul>
Control flow <ul>
@For <li
*ngFor="
let episode of episodes;
@for fournit certaines informations sur la let i = index;
let isOdd = odd;
boucle en cours : let isFirst = first
"
$index: position de l'élément. [ngClass]="{ odd: isOdd, bgfonce: isFirst }"
>
Episode {{ i + 1 }}{{ [Link] }}
$odd: true si l'élément est à une position impaire. </li>
</ul>
$even: true si l'élément est à une position paire. <ul>
@for (episode of episodes; track [Link]) {
$first: true si l'élément est à la première position. <li
[ngClass]="{ odd: $odd, bgfonce: $first }"
>
$last: true si l'élément est à la dernière position. Episode {{ $index + 1 }} : {{ [Link] }}
</li>
}
</ul>

82
Control flow 8
3

@For
track
Avec @For la fonction de tracking est devenue obligatoire
La fonction de suivi créée via l'instruction track est utilisée pour
permettre au mécanisme de détection des changements
d’Angular de savoir exactement quels éléments mettre à jour
dans le DOM après les modifications de l’itérable d'entrée.
La fonction de suivi indique à Angular comment identifier de
manière unique un élément de la liste.
@for (episode of episodes; track [Link]) {
<li>{{ [Link] }}</li>
}
Control flow 8
4

@For
track
En principe, il devrait toujours y avoir quelque chose d'unique dans
les éléments sur lesquels vous itérer.
Dans le pire des cas, s'il n'y a rien d'unique dans les éléments du
tableau, vous pouvez utiliser $index de l'élément, c'est-à-dire la
position de l'élément dans le tableau.

@for (episode of episodes; track $index) {


<li>{{ [Link] }}</li>
}
</ul>
Control flow 8
5

@For
track
 track peut prendre en paramètre une fonction à laquelle on passe
l’index et l’élément actuel de l’itération

@for (episode of episodes; track trackFn($index, episode)) {


<li>{{ [Link] }}</li>
}

trackFn(index: number,episode: { id: number; title: string; }){


return [Link];
}
8

Control flow
6

@For, la gestion d’un itérable vide


Afin de gérer le cas ou votre itérable est vide, nous avons le block
@empty.
Ce bloque n’est activé que si l’itérable sur lequel vous bouclez est vide.
<ol class="list-group">
@for (player of players; track [Link]) {
<li class="list-group-item">{{[Link]}}</li>
}
@empty {
<li class="list-group-item list-group-item-danger">La liste ne nous est
pas encore parvenue</li>
}
</ol>
8

Control flow
7

@switch
<div [ngSwitch]="streamingService">
Avec @switch, vous pouvez <div *ngSwitchCase="'AppleTV'">Ted Lasso</div>
<div *ngSwitchCase="'Disney+'">Mandalorian</div>
créer des switch très simplement. <div *ngSwitchDefault>Peaky Blinders</div>
</div>
Vous avez les trois opérateurs : @switch(streamingService) {
@case ('Disney+') {
@switch, @case, @default <div>'Mandalorian'</div>
} @case ('AppleTV') {
@switch : définir l’élément sur <div>'Ted Lasso'</div>
} @default {
lequel switcher <div>'Peaky Blinders'</div>
}
@case : pour identifier le cas }

@default : pour les valeurs par @Component({


défaut standalone: true,
imports: [NgSwitch, NgSwitchCase, NgSwitchDefault],
})
export class SwitchComponent {
streamingService = 'Netflix';
}
8

Control flow
8

Migration automatique
Si vous voulez migrer votre ancien code et utilisez le nouveau work
flow, angular vous fournie une commande qui le fait pour vous :
ng g @angular/core:control-flow
Cette fonctionnalité est encore en developer Preview (Anguar 17.2)
Exercice
 Soit la classe Product :
 Créer un composant qui contient la propriété products qui représente un
tableau de product.
 Afficher la liste des products avec les règles suivantes :
 Si la quantité du produit est égale à 0 afficher ‘Le produit est en rupture de
stock’
 Si la quantité est > 0 et <= 3 afficher ‘il ne reste que n produits en vente’
 Si il y a un discount afficher le prix initial barré et le prix après discount
 S’il n’y a aucun produit en vente afficher le magasin est vide.
export class Product {
constructor(
public id = 0, public label = "", public price = 0, public quantity = 0, public discount = 0
) {}
} 89
Exercice
products: Product[] = [
new Product(1, 'Angular Book', 100, 5, 20),
new Product(2, 'Nest Book', 80, 2, 0),
new Product(3, 'Symfony Book', 55, 0, 0),
new Product(4, 'Js Book', 120, 20, 30),
];

90
Change Detection
C’est quoi ?
 Change Detection est l’un des mécanisme les plus importants dans
Angular.
 Il permet de suivre les changements d’état de votre application et
d’afficher les modifications dans votre vue.
 Il garantit que l’interface utilisateur suit toujours d’une façon
synchrone l’état interne de votre application.

91
Change Detection
Chaque composant dans Angular est représenté par une structure
appelé View. Elle contient entre autre l’instance de la classe
Composant appelée componentInstance ainsi que la liste des
éléments du DOM représentant le Template.

[Link] 92
Change Detection
Ensuite, quand le compilateur traite le composant, il identifie les
élément qui nécessite un changement lors du changement d’état.

93
Change Detection
 Pour chacun de ces éléments, il crée des objets Bindings. C’est une
structure de données qui informe sur deux choses :
Que voulons nous mettre à jour dans le Dom
Ou récupérer la nouvelle valeur

94
Change Detection
Ensuite, dès qu’un change
Detectin est déclenché,
Angular va parcourir
l’ensemble des Views
(Component) et évaluer la
nouvelle expression du
Binding et la comparer à la
précédente.
Si la valeur est modifiée, elle
met à jour le DOM.

95
Change Detection
Quand déclencher un Change Detection
 Un Change Detection est déclenché dans ces cas d’utilisation
1. Initialisation des composants. Par exemple, lors du lancement d'une
application angular, Angular charge le composant principal et
déclenche [Link]() pour appeler la détection de
changement et le rendu de la vue.
2. Les event listener du DOM peuvent mettre à jour les données dans
un composant Angular et déclencher le Change Detection.
3. Les requêtes HTTP.

[Link] 96
Change Detection
Quand déclencher un Change Detection
4. Les MacroTasks, tells que setTimeout() ou setInterval(). En effet
vous pouvez mettre à jour les données dans la callback function d’une
macroTask comme setTimeout().
5. Les MicroTasks, comme [Link]() dont les callback peuvent
mettre à jour les données.
6. D’autres opérations asynchrones qui peuvent mettre à jour vos
données telles que [Link]() et [Link]().

[Link] 97
Change Detection
La stratégie par défaut
 Ici, dans tous les composants de
l’arbre de composant, le change
detector alloué à chaque
composant, compare la valeur
courante et la valeur précédente des
propriétés.
Si la valeur change, il va marquer
une propriété isChanged à true.

98
Change Detection

[Link] 99
1

Change Detection
0
0

Problème
Le problème majeur d’Angular était son incapacité à détecter le
changement et ou il se produit exactement.
Ceci est la cause majeure du parcours de l’intégralité de l’arbre afin
d’identifier l’endroit exact ou le changement s’est effectué.
 Ceci permet à angular de minimiser la mise à jour du DOM qui est
une opération très couteuse.
1
0
1

Change Detection
Quand un événement se déclenche dans votre application, Angular
parcourt tout votre arbre de composants pour chercher où les
modifications doivent être effectuées.
Ce parcours, Angular le fait de manière très rapide mais le processus
pourrait être encore plus rapide.
D’où l’intérêt d’utiliser les signaux, qui vont assister Angular en lui
indiquant où exactement il doit checker les changements.
Les changements peuvent être opérés par Angular dans une petite
partie d’un template (un bloc @if ou @for).
1
0
2

Performances
Les signaux permettent de réduire le nombre de calculs effectués
lors de la détection des changements dans une application Angular.
Cela se traduit par de meilleures performances d’exécution.
Avec les signaux, il sera possible de vérifier les changements
uniquement dans les composants concernés.
Les signaux vont permettre de rendre [Link] facultatif dans les
versions futures d'Angular. [Link] est une bibliothèque utilisée par
Angular pour détecter les changements et exécuter les tâches
asynchrones
AYMEN SELLAOUTI

103
1
0
4

Qu’est ce qu’un Signal ?


 Un signal agit comme une enveloppe (wrapper) autour d'une valeur, ayant la
capacité d'avertir les consommateurs lorsque la valeur change.

 Un signal est une primitive réactive qui représente une valeur et qui nous
permet de

 suivre ses changements au fil du temps.

 modifier cette même valeur en notifiant tous ceux qui en dépendent.

 Elle permet de définir l’état réactif de votre application et d’avoir une


identifications précise de quels composants sont impactés par un
changement.
1
0
5

Qu’est ce qu’un Signal ?


 Les signaux sont un concept utilisé dans plusieurs frameworks (Qwik, SolidJs,
Vue, KnockoutJs)

 Le concept du Signal dans Angular est une fonctionnalité introduite en


‘Developer Preview’ dans la version 16 de la bibliothèque @angular/core et
stable à partir de Angular 17.

 Il a pour objectif de simplifier le développement en donnant une alternative


plus simple que RxJS pour gérer certains cas de réactivité d’un façon plus
simple.
1
0
6

Pourquoi intégrer les signaux

Les signaux permettent de Les signaux boostent les Les signaux pèsent 2KB,
réagir aux changements performances de votre n’ont pas besoin de charger
d’état (State) n’importe où application en réduisant le des dépendances tierces et ne
dans notre code et pas travail que fait Angular représentent aucun coût de
seulement au sein d’un pour garder le DOM à jour démarrage lorsque votre
composant. avec les valeurs des données application se charge.
1
0
7

API
Angular propose trois principales primitives pour utiliser les signaux :
signal
computed
effect
Créer un signal via l’api signal()
La fonction signal permet d’initialiser une variable et d’informer
Angular et son contexte à chaque fois que sa valeur change.
Elle retourne un objet de type WritableSignal.
 Un signal doit avoir une valeur initiale.
@Component({
selector: 'app-signal-api',
standalone: true,
imports: [],
styleUrl: './[Link]',
template: ` <h1>Hello World</h1> `,
})
export class SignalApiComponent {
lastname: WritableSignal<string> = signal('sellaouti');
} 108
Récupérer la valeur d’un signal
Afin de récupérer la valeur d’un signal il suffit de l’appeler comme
une fonction.
@Component({
selector: 'app-signal-api',
standalone: true,
imports: [],
styleUrl: './[Link]',
template: ` <h1>Hello {{ lastname() }}</h1> `,
})
export class SignalApiComponent {
lastname: WritableSignal<string> = signal('sellaouti');
}

109
Les méthodes de modifications de signal :
set() et update() @Component({
selector: 'app-signal-api',
Pour modifier la valeur d’un standalone: true,
imports: [],
signal, on peut passer par : template: `
<h1>Hello {{ lastname() }}</h1>
<input type="number" #input
La Méthode set, qui permet (change)="setCounter(+[Link])"
/>
d’affecter une nouvelle <h2 (click)="increment()">
valeur au signal. Click here. You clicked {{ counter() }} times
</h2>
`,
La méthode update, qui })
export class SignalApiComponent {
permet de calculer une lastname = signal('aymen');
counter = signal(0);
nouvelle valeur d’un signal increment() {

en fonction de sa valeur }
[Link]((currentValue) => currentValue + 1);

précédente. setCounter(val: number) {


[Link](val);
}
}
110
Exercice
 Reprenez L’exercice ‘ColorComponent’ en utilisant les signaux.

111
computed()
L’api computed() permet de créer un nouveau signal dont la valeur
dépend d’autres signaux.
Lorsqu'un signal est mis à jour, tous ses signaux dépendants
seront alors automatiquement mis à jour.
On note que computed() retourne un objet de type Signal et non
WritableSignal.
lastname = signal('aymen');
firstname = signal('sellaouti');
fullname = computed(() => `${[Link]()} ${[Link]()}`)

112
computed()
Pour identifier un signal qui a changé, et donc si on doit exécuté un computed,
Angular utilise [Link].
[Link]() permet de déterminer si deux valeurs sont identiques. Deux valeurs sont
considérées identiques si :
elles sont toutes les deux undefined
elles sont toutes les deux null
elles sont toutes les deux true ou toutes les deux false
elles sont des chaînes de caractères de la même longueur et avec les mêmes
caractères (dans le même ordre)
elles sont toutes les deux le même objet (même référence)
elles sont des nombres et
sont toutes les deux égales à +0
sont toutes les deux égales à -0
sont toutes les deux égales à NaN
[Link] 113
computed()
Comment ca marche ? @Component({
template: `
L’arbre de dépendance est <h3>Counter value {{ counter() }}</h3>
crée dynamiquement à <h3>Derived counter: {{ derivedCounter() }}</h3>
<button (click)="increment()">Increment</button>
chaque appel du computed. <button (click)="multiplier = 10">Set multiplier to 10</button>
`,})
export class ComputedProblemComponent {
Il faut donc faire très counter = signal(0);
multiplier: number = 0;
attention dans la définition derivedCounter = computed(() => {
de vos computed lorsqu’il y a if ([Link] < 10) {
return 0;
des traitement conditionnel. } else {
return [Link]() * [Link];
}
});
increment() {
[Link](`Updating counter...`);
[Link]([Link]() + 1);
}
}

[Link] 114
computed()
Exclure un signal de l’arbre de dépendence
Si pour une raison ou une autre vous voulez lire une valeur d’un signal
dans un comptued mais sans l’intégrer dans l’arbre de dépendance,
vous pouvez utiliser l’Api untracked.
Dans cet exemple le computed fullname ne sera mis à jour que si le
signal firstname change
lastname = signal('sellaouti');
firstname = signal('aymen');
fullname = computed(() => `${[Link]()} ${untracked([Link])}`);

115
computed()
Modifier un signal dans un computed
 La fonction computed doit être sans effets de bord, ce qui signifie qu'elle ne
doit accéder qu'aux valeurs des signaux dépendants (ou à d'autres valeurs
impliquées dans le calcul) et éviter toute mise à jour.
Vous ne pouvez pas modifier un signal dans un computed, ceci provoquera
une erreur.
fullname = computed(() => {
[Link]('i am computing....');
[Link]('ccc');
return `${[Link]()} ${untracked([Link])}`;
});

116
computed()
Les computed, autres propriétés
Les computed sont paresseux (lazy), ce qui signifie que computed
n'est invoquée que lorsque quelqu'un s'intéresse (lit) sa valeur.
Cela permet d'optimiser les performances en évitant les calculs
inutiles.
 Les computed sont automatiquement supprimés lorsque la
référence du signal calculée devient hors de portée.
Cela garantit que les ressources sont libérées et qu’aucune
opération de nettoyage explicite n’est requise.
 Ceci est due à l’utilisation des weakRefrence avec les signaux

[Link] 117
Writablesignals et signals
Par défaut, tous les signaux créés par la fonction Signal sont de type
WritableSignal. Ainsi, n’importe quelle entité peut modifier sa valeur
(via les méthodes set() et update()).
Si on désire interdire toute modification de la valeur d’un signal,
Angular nous propose la méthode asReadOnly().
Les signaux créés par computed sont, par défaut, read only.

private currencies = signal(['USD', 'EUR', 'GBP']);


currencies$ = [Link]();

118
Exercice
 Créer un composant TTC qui permet de calculer le prix TTC d’un produit selon le
nombre de pièces et la TVA. Sachant que l’utilisateur peut changer toutes les
valeurs et que par défaut la quantité est de 1 le prix est de 0 et la tva est de 18.
 Si le nombre de pièces est entre 10 et 15 une remise de 20% est appliquée.
 Si le nombre de pièces est supérieur à 15 une remise de 30% est appliquée.

119
L’API effect()
Dans certains cas d’utilisation, vous avez besoin d’être notifié par le
changement d’un signal pour effectuer un effet de bord, donc faire un
traitement sans pour autant créer un nouveau signal ou en modifier
d’autres.
Pensez à un log, à un calcul d’un nombre de click, faire un appel à une
API pour enregistrer une valeur,…
L’API effect est la afin de vous donner cette possibilité
L’effect va être réexécuté si l’un des signaux qu’il utilise émet une
nouvelle valeur.

120
L’API effect()
Vous pouvez créer un effet via la fonction effect.
Les effets s'exécutent toujours au moins une fois.
Lorsqu'un effet est exécuté, il suit tous les signaux qu’il contient.
Chaque fois que l'une de ces valeurs de signal change, l'effet se
reproduit.
L’effet est semblable aux computed, les effets gardent une trace de
leurs dépendances de manière dynamique et ne suivent que les
signaux qui ont été lus lors de l'exécution la plus récente.
constructor() { private logEffect = effect(() => {
effect(() => { [Link](`
[Link](`count:${[Link]()}`); The current count is:${[Link]()}
}); `);
} }); 121
L’API effect()
Les effets s'exécutent toujours de
manière asynchrone, pendant le export class EffectComponent {
processus de change detection. counter = signal(0);
constructor() {
Les effets seront exécutés le nombre [Link](1);
[Link](2);
minimum de fois. Si un effet dépend }
de plusieurs signaux et que plusieurs private logEffect = effect(() => {
d'entre eux changent simultanément, [Link](`
une seule exécution de l'effet sera The current count is:
${[Link]()}`);
programmée. });
Remarque : l'API effect() est toujours en }
aperçu développeur (17.3).
122
L’API effect()
Un effect doit être définit dans un contexte d’injection.
Ceci est faisable dans un component, un pipe, une directive ou le
constructeur d’un service.
 Vous pouvez aussi injecter l’Injector et le passer à l’effect en
deuxième paramètre qui représente un objet d’options.
private logEffectWithInjector = effect(() => {
[Link](
`The current count is: ${[Link]()}`
);
},
{ injector: [Link] }
);
123
effect()
Exclure un signal de l’arbre de dépendence
Si pour une raison ou une autre vous voulez lire une valeur d’un signal
dans un effect mais sans l’intégrer dans l’arbre de dépendance, vous
pouvez utiliser l’Api untracked.
Untracked peut prendre en paramètre une fonction

/**
* Execute an arbitrary function in a non-reactive (non-tracking) context. The
* executed function can, optionally, return a value.
*/
export declare function untracked<T>(nonReactiveReadsFn: () => T): T;

124
Cycle de vie d’un composant
 Le composant possède un cycle de vie géré par Angular. En effet,
Angular :
Crée le composant
L’affiche
Crée ses fils
Les affiche
Ecoute le changement des propriétés
Le détruit avant de l’enlever du DOM

[Link]

125
Comment ca marche réellement
<mon-app>
<compo-fils [binded]='prop'>
1. Angular lance l’application
2. Ils crée les classes pour chaque composant, il lance donc le Constructeur de mon-app component.
3. Il gère toutes les dépendances injectées au niveau du constructeur et les définit comme des
paramètres
4. Il crée le nœud du Dom qui va héberger le composant
5. Il commence ensuite la création du composant fils et appelle son constructeur. Ici la propriété
binded n’est pas prise en considération par Angular.
6. A ce moment Angular lance le processus de détection des changements. C’est ici qu’il mettra à
jour le binding de mon-App et lancera l’appel à ngOnInit, suivi de la gestion du binding de
compo-fils puis l’appel de son ngOnInit.

126
Interfaces de gestion du cycle de vie d’un
composant
 Afin de gérer le cycle de vie d’un composant, Angular nous offre un
ensemble d’interfaces à implémenter pour les différentes étapes du cycle
de vie.
L’ensemble des interfaces est disponible dans la librairie core
d’Angular
Chaque interface offre une seule méthode dont le nom suit la
convention suivante :
ng + NomDeL’Interface
Exemple : Pour l’interface OnInit nous avons la méthode ngOnInit

127
128
129
Interfaces de gestion du cycle de vie d’un
composant
 ngOnChanges: Cette méthode est appelée lors d’un binding d’une
propriété d’une directive. La méthode reçoit en paramètre un objet
représentant les valeurs actuelles et les valeurs précédentes disponibles
pour ce composant. Elle est appelé si un composant possède des input
(@Input) et à chaque fois qu’elles changent.
 ngOnInit: Cette méthode initialise le composant après qu’Angular ait
initialisé les propriétés du composant.
 ngDoCheck: Appelé après chaque change détection.

130
Interaction entre composants
Parent Enfant
[propertyBinding]

@Input
Component Component

(eventBinding)

Template @Output Template

131
Pourquoi ?
Le père voit le fils, le fils ne voit pas le père

Parent import { Component } from '@angular/core';

@Component({
selector: 'app-root',
template: `
Enfant <p>Je suis le composant père </p>
<forma-fils></forma-fils>
`,
styles:
})
export class AppComponent {
title = 'app works !';
}

132
Interaction du père vers le fils
Le père peut directement envoyer au fils des données par Property Binding

Parent import { Component } from '@angular/core';

@Component({
selector: 'app-root',
template: `
Enfant <p>Je suis le composant père </p>
<forma-fils></forma-fils>
`,
styles:
})
export class AppComponent {
title = 'app works !';
}

133
Interaction du père vers le fils
Problème : Le père voit le fils mais pas ces propriétés !!! Solution : les rendre visible avec Input

Parent import { Component } from '@angular/core';

@Component({
selector: 'app-root',
template: `
Enfant <p>Je suis le composant père </p>
<forma-fils></forma-fils>
`,
styles:
})
export class AppComponent {
title = 'app works !';
}

134
Interaction du père vers le fils
Problème : Le père voit le fils mais pas ces propriétés !!! Solution : les rendre visible avec Input

import { Component } from


Parent '@angular/core'; import {Component, Input}
from '@angular/core';
@Component({
selector: 'app-root', @Component({
template: ` selector: 'app-input',
Enfant <p>Je suis le composant père </p> templateUrl:
<forma-fils [external]="title"> './[Link]',
</forma-fils> styleUrls:
`, ['./[Link]']
styles: })
}) export class InputComponent
export class AppComponent { {
title = 'app works !'; @Input() external:string;
} }

135
Two way Binding
[(ngModel)]

136
Signal Input
 Pour que l’exemple précédent fonctionne, on avait deux méthodes :
 Utiliser un setter
 Utiliser le OnChange hook
 La version 17.1 a vu Angular introduire le concept de Signal Input pour améliorer
l’interaction entre les composants.
 Les Signal Input permettent de lier les valeurs des composants parents. Ces
valeurs sont exposées à l'aide d'un signal et peuvent changer au cours du cycle de
vie de votre composant.
import { Input, input } from '@angular/core';
isEvenn!: boolean;
// Sans Signal Input
@Input() isEven: number;
// Avec les signal Input
counter = input<number>();
137
Signal Input
 Les Signal Input permettent d’exposer des // optional
paramètres au composant parent afin qu’il counter = input<number>();
puisse paramétrer le composant fils. // Required
counter = [Link]<number>();
 Les Signal Input sont en lecture seule. // Valeur par défaut = 0
Seul Angular peut changer leur valeur suite à counter = [Link]<number>(0);
un changement côté parent.
 Le Signal Input peut être optionnel ou counter = input(0,{
required alias: 'counter',
transform: (value: number) => value * 100,
 Il peut avoir une valeur par défaut });

 Il peut avoir une Alias counter = [Link]({


alias: 'counter',
 Et vous pouvez le transformer transform: (value: number) => value * 100,
});
138
Exercice
 Créer un composant fils

 Récupérer la couleur du père dans le composant fils

 Faite en sorte que le composant fils affiche la couleur du background de son père

139
Interaction du fils vers le père
L’enfant ne peut pas voir le parent. Appel de l’enfant vers le parent. Que faire ?

Parent
import {Component} from '@angular/core';
@Component({
selector: 'bind-output',
Enfant template: `Bonjour je suis le fils`,
styleUrls: ['./[Link]']
})
export class OutputComponent {
valeur:number=0;
}
}

140
Interaction du fils vers le père
Solution : Pour entrer c’est un input pour sortir c’est surement un output. Externaliser un
évènement en utilisant l’Event Binding. import {Component, EventEmitter, Output} from
'@angular/core';
Parent @Component({
selector: 'bind-output',
template: `
<button (click)="incrementer()">+</button> `,
styleUrls: ['./[Link]']
Enfant })
export class OutputComponent {
valeur:number=0;
// On déclare un evenement
@Output() valueChange=new EventEmitter();
Au click sur l’event incrementer(){
valueChange je vais [Link]++;
emmètre une réponse [Link]
contenant ce que je veux [Link]);
}
envoyer à mon père
}}

141
Interaction du père vers le fils
La variable $event est la variable utilisée pour faire transiter les informations.
import {Component, EventEmitter, Output} from import { Component } from
'@angular/core'; '@angular/core';
Parent @Component({ @Component({
selector: 'bind-output', selector: 'app-root',
template: ` template: `
<button (click)="incrementer()">+</button> `, <h2> {{result}}</h2>
Enfant styleUrls: ['./[Link]'] <bind-output
}) (valueChange)="showValue($event)
export class OutputComponent { "></bind-output>
valeur:number=0; `,
Mon père va ensuite // On déclare un evenement styles: [``],
@Output() valueChange=new EventEmitter(); })
intercepter l’event et export class AppComponent {
incrementer(){
récupérer ce que je lui [Link]++; title = 'app works !';
ai envoyé à travers la [Link]( result:any='N/A';
variable $event et va [Link] showValue(value){
l’utiliser comme il veut ); [Link]=value;
} }
}} } Parent
Enfant
142
Signal Output
 L'API output() remplace directement le décorateur @Output() traditionnel.
 Le décorateur @Output n’est pas obsolète
 Angular a donc ajouté output() comme nouvelle façon de définir les sorties des
composants dans Angular, d'une manière plus sûre et mieux intégrée à RxJ que
l'approche traditionnelle @Output et EventEmitter.
 La syntaxe a été simplifiée. /* Sans les signal Output */
@Output() oldSelectCv = new EventEmitter<Cv>();
/* Avec les signal Output */
selectCv = output<Cv>();
<app-item (selectCv)="onSelectCv($event)" [cv]="cv" /> onClick() {
Pere if ([Link]) {
/* Cette partie du code reste la même */
[Link]([Link]);
}
Fils 143
Exercice

 Ajouter une variable myFavoriteColor dans le composant du fils.

 Ajouter un bouton dans le composant Fils

 Au click sur ce bouton, la couleur de background du Div du père doit prendre la


couleur favorite du fils.

144
Exercice
 Le but de cet exercice est de AppComponent
créer une mini plateforme de CvComponent
ListeComponent
recrutement. DetailComponent
 La première étape est de créer ItemComponent
la structure suivante avec une
vue contenant deux parties :
 Liste des Cvs inscrits

 Détail du Cv qui
apparaitra au click
 Il vous est demandé juste
d’afficher un seul Cv et de lui
afficher les détails au click.
Il faudra suivra cette architecture.

145
Exercice
 Le but de cet exercice est de créer
une mini plateforme de
recrutement.

 La première étape est de créer la


structure suivante avec une vue
contenant deux parties :
 Liste des Cvs inscrits

 Détail du Cv qui apparaitra


au click

 Il vous est demandé juste d’afficher


un seul Cv et de lui afficher les détails
au click.
Il faudra suivra cette architecture.

146
Exercice
AppComponent
CvComponent
ListeComponent
DetailComponent
ItemComponent

147
Exercice
AppComponent
CvComponent
ListeComponent
DetailComponent
ItemComponent

Au click sur le Cv les détails sont affichés

148
Exercice
Un cv est caractérisé par :
id
name
firstname
Age
Cin
Job
path

Au click sur le Cv les détails sont affichés

149
Angular
Les directives
AYMEN SELLAOUTI

150
Plan du Cours
1. Introduction
2. Les composants
3. Les directives
3 Bis. Les pipes
4. Service et injection de dépendances
5. Le routage
6. Form
7. HTTP
8. Les modules
9. Les tests unitaires
151
Objectifs
1. Comprendre la définition et l’intérêt des directives.

2. Voir quelques directives d’attributs offertes par angular et savoir les utiliser

3. Créer votre propre directive d’attributs

4. Voir quelques directives structurelles offertes par angular et savoir les utiliser

152
Qu’est ce qu’une directive
 Une directive est une classe permettant import { Directive } from '@angular/core';
d’attacher un comportement aux
éléments du DOM. Elle est décorée @Directive({
avec l’annotation @Directive. selector: '[appHighlight]',
standalone: true
})
 Apparait par défaut dans un élément export class HighlightDirective {
comme un tag (comme le font les
attributs). constructor() { }

 La command pour créer une directive est }


 ng g d nomDirective
<div appHighlight>
Bonjour je teste une directive
</div>

153
Qu’est ce qu’une directive
 La documentation officielle d’Angular identifie trois types de directives :

 Les composants qui sont des directives avec des templates.

 Les directives structurelles qui changent l’apparence du DOM en ajoutant et


supprimant des éléments.

 Les directives d’attributs (attribute directives) qui permettent de changer l’apparence


ou le comportement d’un élément.

154
Les directives d’attribut (ngStyle)
 Cette directive permet de modifier l’apparence de l’élément cible.

 Elle est placé entre [ ] [ngStyle]

 Elle prend en paramètre un attribut représentant un objet décrivant le style à


appliquer.

 Elle utilise le property Binding.

155
Les directives d’attribut (ngStyle)
import { Component} from import { Component} from
'@angular/core'; '@angular/core';
@Component({
@Component({ selector: 'direct-direct',
selector: 'direct-direct', template: `
template: ` <p [ngStyle]="{'color':myColor,'font-
<p [ngStyle]="{'color':'red', family':myfont,'background-color' :
'font-family':'garamond', myBackground}">
'background-color' : 'yellow'}"> <ng-content></ng-content>
<ng-content></ng-content> </p>`,
</p> styleUrls: ['./[Link]']
`, })
styleUrls: ['./[Link]'] export class DirectComponent{
}) private myfont:string="garamond";
export class DirectComponent{ private myColor:string="red";
} private myBackground:string="blue"
}
156
Les directives d’attribut (ngStyle)

157
Les directives d’attribut (ngStyle)
import {Component, Input} from
'@angular/core';
@Component({ @Component({
selector: 'app-root', selector: 'direct-direct',
template: ` template: `
<direct-direct [myColor]="gray">J'ai <p [ngStyle]="{'color':myColor,
été stylé par ngStyle</direct-direct> 'font-familly':myfont,
`, 'background-color' : myBackground}">
styles: [` <ng-content></ng-content>
h1 { font-weight: normal; } </p>
p{color:yellow;background-color: red} `,
`], styleUrls: ['./[Link]']
}) })
export class AppComponent { export class DirectComponent{
private myfont:string="garamond";
} @Input() private myColor:string="red";
private myBackground:string="blue"
}

158
Les directives d’attribut (ngStyle)

159
Exercice
 Nous voulons simuler un Mini Word pour gérer un paragraphe en utilisant ngStyle.
 Préparer un input de type texte, un input de type number, et un select box.
 Faite en sorte que lorsqu’on écrive une couleur dans le texte input, ca devienne la couleur du
paragraphe. Et que lorsque on change le nombre dans le number input la taille de l’écriture.
 Finalement ajouter une liste et mettez y un ensemble de police. Lorsque le user sélectionne une
police dans la liste, la police dans le paragraphe change.

160
Les directives d’attribut (ngClass)
 Cette directive permet de modifier l’attribut class.

 Elle cohabite avec l’attribut class.

 Elle prend en paramètre


 Une chaine (string)
 Un tableau (dans ce cas il faut ajouter les [ ] donc [ngClass]
 Un objet (dans ce cas il faut ajouter les [ ] donc [ngClass]

 Elle utilise le property Binding.

161
Les directives d’attribut (ngClass)
import {Component, Input} from '@angular/core'; // Tableau
@Component({ <div [ngClass]="['colorer', 'arrierplan'] "
selector: 'direct-direct',
class="encadrer">
template: `
// Objet
<div ngClass="colorer arrierplan" class="encadrer">
test ngClass <div [ngClass]="{ colorer: isColoree,
</div> arrierplan: isArrierPlan} "
`, class="encadrer">
styles: [`
.encadrer{ border: inset 3px black; }
.colorer{ color: blueviolet; }
.arrierplan{background-color: salmon; }
`]
})
export class DirectComponent{
private myfont:string="garamond";
@Input() private myColor:string="red";
private myBackground:string="blue«
private isColoree:boolean=true;
private isArrierPlan:boolean=true
}
162
Exercice

 Préparer 3 classes présentant trois thèmes différents (couleur font-size et font-police)

 Au choix du thème votre cible changera automatiquement

163
Customiser un attribut directive
 Afin de créer sa propre « attribut directive » il faut utiliser un HostBinding sur
la propriété que vous voulez binder.
 Exemple : @HostBinding('[Link]')
bg:string="red";

 Si on veut associer un événement à notre directive on utilise un HostListner


qu’on associe à un événement déclenchant une méthode.
 Exemple : @HostListener('mouseenter') mouseover(){
[Link] =[Link];
}
Afin d’utiliser le HostBinding et le HostListner il faut les importer du core d’angular

164
Exercice
Un truc plus sympas on va créer un simulateur d’écriture arc en ciel.
Créer une directive
Créer un hostbinding sur la couleur et la couleur de la bordure.
Créer un tableau de couleur dans votre directive.
Faite en sorte qu’en appliquant votre directive à un input, à chaque
écriture d’une lettre (event keyup) la couleur change en prenant
aléatoirement l’une des couleurs de votre tableau. Pensez à utiliser
[Link]() qui vous retourne une valeur entre 0 et 1.

165
Exercice

166
Customiser une attribut directive
 Nous pouvons aussi utiliser le input afin de rendre notre directive paramétrable

 Tous les paramètres de la directive peuvent être mises en input puis récupérer à
partir de la cible.

 Exemple

 Dans la directive @Input() private myColor:string="red";

 <direct-direct [myColor]="gray">

167
Les directives structurelles
 Une directive structurelle permet de modifier le DOM.

 Elles sont appliquées sur l’élément HOST.

 Elles sont généralement précédées par le préfix *.

 Les directives les plus connues sont :

 *ngIf

 *ngFor

 [ngSwitch]

168
Les directives structurelles *ngIf
 Prend un booléen en paramètre.

 Si le booléen est true alors l’élément host est visible

 Si le booléen est false alors l’élément host est caché

Exemple
<p *ngIf="true">
Je suis visible :D</p>
<p *ngIf="false">
Le *ngIf c'est faché contre
moi et m'a caché :(
</p>

169
Exercice
Teston *ngIf en créant un composant contenant un bouton et un
paragraphe.

Le bouton s’appellera ‘Click moi’

Un paragraphe contenant une phrase

Au click, si le paragraphe est caché on l’affiche, s’il est affiché, on le


cache

170
Les directives structurelles *ngFor
 Permet de répéter un élément plusieurs fois dans le DOM.

 Prend en paramètre les entités à reproduire. <ul>


<li *ngFor="let episode of episodes">
{{[Link]}}
 Fournit certaines valeurs : </li>
</ul>
 index : position de l’élément courant
 first : vrai si premier élément <ul>
<li *ngFor="let episode of episodes; let i = index;
 last vrai si dernier élément let isOdd = odd; let isFirst=first"
 even : vrai si l’indice est paire [ngClass]="{ odd: isOdd , bgfonce: isFirst}"
 odd : vrai si l’indice est impaire >
Episode {{i+1}}{{[Link]}}
</li>
</ul>

171
Angular
Les pipes
AYMEN SELLAOUTI

172
Références

173
Plan du Cours
1. Introduction
2. Les composants
3. Les directives
3 Bis. Les pipes
4. Service et injection de dépendances
5. Le routage
6. Form
7. HTTP
8. Les modules
9. Les tests unitaires
174
Objectifs
1. Définir les pipes et l’intérêt de les utiliser

2. Vue globale des pipes prédéfinies

3. Créer un pipe personnalisé

175
Qu’est ce qu’un pipe
Un pipe est une fonctionnalité qui permet de formater et de
transformer vos données avant de les afficher dans vos Templates.
Exemple l’affichage d’une date selon un certain format.
Il existe des pipes offerts par Angular et prêt à l’emploi.
Vous pouvez créer vos propres pipes.

<input type="text" [(ngModel)]="pipeVar" class="form-control">


<br>Avec le pipe uppercase : {{pipeVar|uppercase}}<br>
Sans aucun pipe : {{pipeVar}}
176
Syntaxe
 Afin d’utiliser un pipe vous utilisez la syntaxe suivante :

 {{ variable | nomDuPipe }}

 Exemple : {{ maDate | date }}

 Afin d’utiliser plusieurs pipes combinés vous utilisez la syntaxe suivante :

 {{ variable | nomDuPipe1 | nomDuPipe2 | nomDuPipe3 }}

 Exemple : {{ maDate | date | uppercase }}

177
Les pipes disponibles par défaut (Built-in
pipes)
 La documentation d’angular vous offre la liste des pipes prêt à l’emploi.

[Link]
 uppercase
 lowercase
 titlecase
 currency
 date
 json
 percent
 …

178
Paramétrer un pipe
 Afin de paramétrer les pipes ajouter ‘:’ après le pipe suivi de votre paramètre.

 {{ maDate | date:"MM/dd/yy" }}

 Si vous avez plusieurs paramètres c’est une suite de ‘:’

 {{ nom | sli[Link] }}

179
Pipe personnalisé
 Un pipe personnalisé est une classe décoré avec le décorateur @Pipe.

 Elle implémente l’interface PipeTransform

 Elle doit implémenter la méthode transform qui prend en paramètre la valeur


cible ainsi qu’un ensemble d’options.

 La méthode transform doit retourner la valeur transformée

 Le pipe doit être déclaré au niveau de votre module de la même manière qu’une
directive ou un composant.
 Pout créer un pipe avec le cli : ng g p nomPipe
180
Exemple de pipe
import { Pipe, PipeTransform } from
<li>
'@angular/core';
<ol *ngFor="let team of
teams">
@Pipe({
{{team | team}}
name: 'team’,
</ol>
standalone: true,
</li>
})
export class TeamPipe implements PipeTransform {
ngOnInit() {
transform(value: any, args?: any): any { [Link] = ['milan', 'barca', 'roma'];
switch (value) { }
case 'barca' : return ' blaugrana';
case 'roma' : return ' giallorossa';
case 'milan' : return ' rossoneri';
}
}

181
Pipe pure et impure
 Par défaut un pipe t considéré comme une fonction pure
 Une fonction est dite pure si elle :
 Ne provoque pas de side effect.
 Renvoie la même valeur pour les mêmes paramètres.
 Lorsque le pipe est pur, la méthode transform() est invoquée
uniquement lorsque ses arguments d'entrée changent.
@Pipe({
name: ‘myPipe’,
standalone: true,
pure: true
}) 182
Exercice
 Créer un composant contenant un input de type texte et une ol
 A chaque fois que vous écrivez dans l’input, afficher le contenu dans
un paragraphe (utilisez ngModel).
 Dans le composant initialiser un tableau avec 100 valeurs entre 20 et
30.
 Dans le ol afficher la valeur de chacun des éléments du tableau et en
faça la valeur de cet élément lorsque on lui applique la fonction suivante:
 f(x) = 2f(x-1) + 3f(x-2) si n> 1 et f(n) = 1 si n== 0 ou 1
 Tester l’input. Que remarquez vous lorsque vous écrivez dans l’input ?

183
Memo
 Il existe une bibliothèque memo-decorator qui permet de mémoriser
des fonctions pures.
 Elle permet en l’utilisant de cacher les résultats des fonctions pures et
de les réutiliser.
 Pour l’installer utiliser la commande npm i memo-decorator

import memo from 'memo-decorator';


@memo()
pureFonction(n: number): number {//…}
184
Exercice
 Utilisez les pipes et utiliser la bibliothèque memo pour optimiser votre
pipe

185
Exercice
Créer un pipe appelé defaultImage qui retourne le nom d’une image par
défaut que vous stockerez dans vos assets au cas ou la valeur fournie au
pipe est une chaine vide ou ne contient que des espaces.

186
Angular
Service et injection de
dépendances
AYMEN SELLAOUTI

187
Références

188
Plan du Cours
1. Introduction
2. Les composants
3. Les directives
3 Bis. Les pipes
4. Service et injection de dépendances
5. Le routage
6. Form
7. HTTP
8. Les modules
9. Les tests unitaires
189
Objectifs
1. Définir un service

2. Définir ce qu’est l’injection de dépendance

3. Injecter un service

4. Définir la portée d’un service

5. Réordonner son code en utilisant les services

190
Qu’est ce qu’un service ?
Un service est une classe qui permet d’exécuter un traitement.

Permet d’encapsuler des fonctionnalités redondantes permettant ainsi


d’éviter la redondance de code.

Component 1 Component 2 Component 3


f(){}; f(){}; f(){};
g(){}; g(){}; g(){};
k(){}; l(){}; m(){};

Redondance de code Maintenabilité difficile

191
Qu’est ce qu’un service ?
f(){};
g(){};

Component 1 Component 2 Component 3


k(){}; l(){}; m(){};

192
Qu’est ce qu’un service ?
Un service est un médiateur entre la vue et la logique

Fournit un ensemble de fonctionnalités

193
Qu’est ce qu’un service ?
Un service peut donc :

Interagir avec les données (fournit, supprime et modifie)

Interaction entre classes et composants

Tout traitement métier (calcul, tri, extraction …)

194
Création d’un service

 Via CLI

 ng generate service nomDuService

 ng g s nomDuService

195
Premier Service

import { Injectable } from '@angular/core';

@Injectable()
export class FirstService {

constructor() { }

196
Injection de dépendance (DI)
 L’injection de dépendance est un patron de conception.
Classe A1{
Classe A2{ Classe A3{
ClasseB b;
ClasseB b; ClasseC c;
ClasseC c;
… …

} }
}

 Que se passera t-il si on change quelque chose dans le constructeur


de B ou C ?
 Qui va modifier l’instanciation de ces classes dans les différentes
classes qui en dépendent?
197
Injection de dépendance (DI)
 Déléguer cette tache à une entité tierce.

Classe A1{ Classe A2{ Classe A3{


Constructor(B b, C c) Constructor(B b) Constructor(C c)
… … …
} } }

INJECTOR

198
Injection de dépendance (DI)

199
Injection de dépendance (DI)
 Comment les injecter ?

 Comment spécifier à l’injecteur quel service et ou est-il visible ?

200
Injection de dépendance (DI)
 L’injection de dépendance utilise les étapes suivantes :

 Déclarer la dépendance dans le provider du module ou du


composant

 Passer la dépendance comme paramètre du constructeur de


l’entité qui en a besoin.

201
Injection de dépendance (DI)
Que peut on injecter
 Toute dépendance de votre classe, à savoir :
 Des instances de classes

 Des constantes

202
Injection de dépendance (DI)
Avantages
 L’intérêt de l’injection de dépendance est donc :
 Couplage lâche
 Facilement remplacer une implémentation d'une dépendance
à des fins de test
 Prendre en charge plusieurs environnements d'exécution
 Fournir de nouvelles versions d'un service à un tiers qui utilise
votre service dans sa base de code, etc.

[Link] 203
Injection de dépendance (DI)
Comment ca fonctionne
 Injection de dépendance => Il nous faut donc une dépendance
 Afin de Lier cette dépendance au système d’injection de
dépendance d’Angular nous devons répondre à deux questions
 Comment Angular va créer la dépendance ?
 Quand est ce qu’Angular doit utiliser cette dépendance ?
 Le moyen permettant de spécifier au système d’injection de
dépendance d’Angular comment créer la dépendance est la fonction
Provider factory.
 Une Provider factory est simplement une fonction simple
qu'Angular peut appeler afin de créer une dépendance.
204
Injection de dépendance (DI)
Comment ca fonctionne
 Cette fonction peut être créée implicitement par Angular en
utilisant quelques conventions simples (le cas le plus répondu) ou
par vous même.
 Ceci implique que pour toute dépendance de votre application
quelque soit son type, il existe une Provider Factory qui sait
comment la créer et qui le fait.

205
Injection de dépendance (DI)
Comment ca fonctionne

function todoServiceProviderFactory(): TodoService {


return new TodoService();
}
function todoServiceProviderFactory(http:HttpClient): TodoService {
return new TodoService(http);
}

206
Injection de dépendance (DI)
Associer votre Factory Provider à Angular
Token
 Maintenant il reste à dire à Angular quand utiliser ce provider.
 Donc, nous devons répondre à cette interrogation : Comment
Angular sait-il quoi injecter, et donc quelle Provider factory
appeler pour créer quelle dépendance ?
 Pour ce faire, vous pouvez utilisez des Tokens.
 Un Token peut avoir plusieurs formes et il a pour rôle d’identifier
une Provider Factory.

207
Injection de dépendance (DI)
Associer votre Factory Provider à Angular
Angular injection token
 La première forme de Token est l’Angular injection token
 C’est une instance de la class InjectionToken
 Son rôle est d’identifier le service dans le système d’injection de
dépendance

export const TODOS_SERVICE_TOKEN =


new InjectionToken<TodoService>("TODO_SERVICE_TOKEN");

208
Injection de dépendance (DI)
Associer votre Factory Provider à Angular
Configurer le Provider
 Maintenant que nous avons notre Provider Factory et notre Token,
nous devons configurer Angular pour qu’ils les prennent en
considération.
 Ceci sera fait à travers le Provider qui n’est qu’un objet de
configuration.
 Il peut prend en paramètres 3 clés (pas que):
 provide: qui est notre Token
 useFactory: qui est notre Factory
 deps: un tableau des dépendances de votre factory

209
Injection de dépendance (DI)
Associer votre Factory Provider à Angular
Configurer le Provider
export const TODOS_SERVICE_TOKEN =
new InjectionToken<TodoService>("TODO_SERVICE_TOKEN");
function todoServiceProviderFactory(http:HttpClient): TodoService {
return new TodoService(http);
}

providers: [
{
provide: TODOS_SERVICE_TOKEN,
useFactory: todoServiceProviderFactory,
deps: [HttpClient]
}
]
[Link]
210
Injection de dépendance (DI)
Associer votre Factory Provider à Angular
Injecter la factory
 Il reste une dernière étape, à savoir comment injecter notre
dépendance dans notre classe.
 On utilise le décorateur @Inject au niveau du constructeur et on
lui passe le Token du Provider Factory que nous voulons injecter.

constructor(
@Inject(TODOS_SERVICE_TOKEN) private todoService: TodoService
) {}

211
Les providers personnalisés
Les autres formes de Tokens
 Comme nous l’avons présenté, le Token peut avoir plusieurs
formes. Parmi elles, le nom de la classe.
 Le token peut être aussi une chaine de caractères, mais ceci
est déconseillé afin d’éviter les collisions de noms.
 Le TOKEN doit être unique pour éviter toute collision,
les Provider factory sont stockés dans une map, et si le
provider est simple et qu’il a le même nom, la map ne
contiendra que le dernier provider défini.

212
Les providers personnalisés
Les autres formes de Tokens
providers: [
{
provide: TodoService,
useFactory: todoServiceProviderFactory,
deps: [HttpClient]
}
],

constructor(
@Inject(TodoService) private todoService: TodoService
) {}
213
Les providers personnalisés
useClass
 Une autre option s’offre à vous, et au lieu de spécifier la
Fonction du Provider Factory avec useFactory, vous pouvez
utilisez la clé useClass.
 En utilisant useClass, Angular saura que la valeur que nous
transmettons est un constructeur valide, qu'Angular peut
simplement appeler en utilisant l’opérateur new.

214
Les providers personnalisés
useClass
providers: [
{
provide: TodoService,
useClass: TodoService,
deps: [HttpClient]
}
],

constructor(
@Inject(TodoService) private todoService: TodoService
) {}
215
Les providers personnalisés
useClass
 Une autre fonctionnalité très pratique de useClass est que
pour ce type de dépendances, Angular essaiera de déduire le
Token d'injection au moment de l'exécution en fonction
de la valeur des annotations de type Typescript.
 Cela signifie qu'avec les dépendances useClass, nous n'avons
même plus besoin du décorateur Inject, ce qui explique
pourquoi vous le voyez rarement.

216
Les providers personnalisés
useClass
providers: [
{
provide: TodoService, Le Token est déterminé par
useClass: TodoService, Angular en utilisant le Type
deps: [HttpClient] TodoService
}
],

constructor(private todoService: TodoService) {}

217
@Injectable
C’est un décorateur permettant de rendre une classe injectable
Une classe est dite injectable si on peut y injecter des dépendances
@Component, @Pipe, et @Directive sont des sous classes de
@Injectable(), ceci explique le fait qu’on peut y injecter directement des
dépendances.
 Si vous n’aller injecter aucun service dans votre service, cette
annotation n’est plus nécessaire.
Remarque : Angular conseille de toujours mettre cette annotation.

218
Les providers personnalisés
useClass
 En utilisant le décorateur @Injectable, votre Provider
devient encore plus simple puisqu’on n’a plus à spécifier les
dépendances qui seront directement déterminé au niveau
du constructeur par le Système d’Injection de dépendance
d’Angular
providers: [ providers: [ providers: [
{ { {
provide: TodoService, provide: TodoService, TodoService,
useClass: TodoService, useClass: TodoService, }
deps: [HttpClient] } ],
} ],
],
219
Les providers personnalisés
useClass
 La syntaxe useClass est aussi utile pour injecter dynamiquement une classe.
 Imaginez que le service de log dépend de l’environnement de développement.
@Module({ export const appConfig: ApplicationConfig = {
providers: [ providers: [
{ {
provide: LoggerService,
provide: LoggerService,
useClass:
useClass:
[Link] === 'development'
[Link] === 'development' ? DevelopmentLoggerService
? DevelopmentLoggerService : ProductionLoggerService,
: ProductionLoggerService, }
} ],
], };
}) export class AppModule {}

220
Exercice
 Nous supposons que votre API est encore en phase de teste ou est en
maintenance. Vous voulez continuer à travailler avec votre
CvComponent.
 Créer un service FakeCvService
 Fait en sorte d’avoir une fonction getCvs qui retourne un tableau de
cvs
 Dans votre cvComponent provider ce fake service à la place du vrai
en vous basant sur une configuration.
 Vérifier le bon fonctionnement
 Remarque: pour créer un observable, vous pouvez utilisez l’opérateur
de création of.
221
Les providers personnalisés
multi
 La plupart des dépendances de notre système correspondront à une
seule valeur, comme par exemple une classe.
 Cependant, il y a des occasions où nous voulons avoir plusieurs
instances pour le même provider.
 Pour ce faire, ajouter la clé multi et mettez la à true.
 Au lieu de recevoir une instance, vous recevrez un tableau
d’instance.
const AuthentificationInterceptorProvider = {
provide: HTTP_INTERCEPTORS,
useClass: AuthentificationInterceptor,
multi: true
};
222
Injection de dépendance (DI)

import { Injectable } from '@angular/core';


@Injectable({
providedIn: 'root'
})
export class CvService {
// Ce service est visible pour tout le monde
constructor() { }

223
Injection de dépendance (DI)

export const appConfig: ApplicationConfig = { @Injectable({


providers: [ providedIn: 'root',
CvService })
], export class CvService {}
};

224
Standalone Component
Les composants autonomes
Configurez l’injection de dépendance
 Afin de fournir un provider pour toute l’application, vous pouvez
ajouter en deuxième paramètre de la fonction
bootstrapApplication, un objet d’options.
 Cet objet contient une clé providers qui prend en paramètre un
tableau de providers.
 Vous pouvez aussi récupérer des providers offert par un module
avec la fonction importProvidersFrom(moduleCible).
A partir de la version 15, vous avez certaines fonctions spécifiques
pour les modules les plus utilisés comme le http avec sa fonction
provideHttpClient(), ou provideRouter(APP_ROUTES).
225
Injection de dépendance (DI)
import { Component, OnInit } from '@angular/core';
import {Cv} from './cv';
import {CvService} from "../[Link]";
@Component({
selector: 'app-cv',
templateUrl: './[Link]',
styleUrls: ['./[Link]'],
providers:[CvService] // on peut aussi l’importer ici
})
export class CvComponent implements OnInit {
selectedCv : Cv;
constructor(private monPremierService:CvService) { }
ngOnInit() {
}
}

226
Les providers personnalisés
la fonction inject (après Angular 14)
 La fonction inject vous permet d’injecter un injectable.
 Avant Angular 14 et à partir d’Angular 9, la fonction inject pouvait
être utilisé uniquement dans la factory de l’InjectionToken ou dans le
factory du @Injectable.
A partir d’Angular 14, cette fonction n’est plus limitée aux factory.
Vous pouvez maintenant l’utiliser dans vos composants, directives
et pipes.
 Le premier intérêt est le type safety avec le décorateur @Inject.
 Il facilite aussi l’héritage en externalisant le dépendance du
composant.
227
Chargement automatique du service
 A partir de Angular 6 vous pouvez ne plus utiliser le provider du module afin de
charger votre service mais le faire directement au niveau du service à travers
l’annotation @Injectable et sa propriété providedIn. Vous pouvez charger le service
dans toute l’application via le mot clé root.
 Si vous voulez charger le service dans un module particulier vous l’importer et
vous le mettez à la place de ‘root’. import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class CvService {
constructor() { }
}
228
Chargement automatique du service
providedIn
 La clé providedIn peut prendre les valeurs suivantes :
 root : L'injecteur au niveau de l'application, c’est ce que vous trouvez
dans la plupart des applications.
 platform : Un injecteur de plateforme singleton spécial partagé par
toutes les applications de la page.
 any : Fournit une instance unique dans chaque module chargé d’une
manière lazy tandis que tous les modules chargés en eager partagent
une instance. Cette option est obsolète.
providedIn?: Type<any> | 'root' | 'platform' | 'any' | null;

229
Avantage de l’utilisation du providedIn
 Permettre le Tree-Shaking des services non utilisés : Si le service
n’est jamais utilisé, son code ne sera entièrement retiré du build
final.

230
Exercice
 Créons un service de Todo, le Model et le composant qui va avec. Un Todo est
caractérisé par un nom et un contenu.
 Ce service permettra de faire les fonctionnalités suivantes :

 Logger un Todo

 Ajouter un Todo

 Récupérer la liste des Todos

 Supprimer un Todo

231
Autres providers
 Dans certains cas d’utilisation, l’utilisation standard des
providers ne convient pas, imaginer l’un des cas suivants :
 Vous souhaiter créer une instance personnalisée au lieu de laisser le
container le faire pour vous.
 Vous voulez injecter une bibliothèque externe
 Vous voulez mocker une classe pour le teste
 Vous voulez injecter des instances différentes selon le contexte

 Angular nous permet de définir des providers particuliers
selon votre besoin.
232
Les providers personnalisés
useValue
 La syntaxe useValue est utile pour injecter
 Une valeur constante,
 Une bibliothèque externe
 Remplacer une implémentation réelle par un objet fictif.
providers: [
{
useValue: [{ lundi: ‘Angular' }, { mardi: 'Still Angular' }],
provide: 'TODOS_LIST',
},
TodoService,
],

233
Les providers personnalisés
useValue
 Si vous injecter une classe, l’utilisation de l’injection via le
constructeur reste d’actualité.
 Sinon, pour injecter ce provider utiliser la syntaxe @Inject,
qui prend en paramètre le Token.
providers: [ constructor(
{ @Inject('TODOS_LIST') todoList,
provide: 'TODOS_LIST', ){
useValue: [{ lundi: 'nestJs' }, { mardi: 'Still NestJs' }], [Link]('Fake Todo List', todoList);
}, }
TodoService,
], @Controller('todo')
export class TodoController {
@Inject('TODOS_LIST') todoList;
constructor() {}
234
Exercice
 Provider la fonction uuid
 Faite en sorte de l’utiliser dans le TodoService

235
Les providers personnalisés
useExisting
 Avec useExisting, vous créer un alias pour un provider déjà
existant.
 useExisting est aussi utile lorsque nous voulons créer un
provider basé sur un autre provider existant.
 Un autre cas d’utilisation est lorsque nous avons de
nombreuses méthodes sur le service et que nous ne voulons
en utiliser que quelques-unes. Cela aide à réduire la taille de
l'interface.

236
Les providers personnalisés
useExisting
class CarService { export abstract class CarSizeService {
getWeight(): number {...} abstract getHeight: number;
abstract getWidth: number;
getColor(): string {...} }
getName(): string {...}
getWidth(): number {...}
getHeight(): number { … }
getModel(): string {...}
getYear(): number {...}
...
}
providers: [{ provide: CarSizeService, useExisting: CarService}]

237
DI Hiérarchique
 Dans Angular vous disposez de plusieurs endroits où vous pouvez
définir les fournisseurs pour vos dépendances :
 Module
 Composant
 Directive !

238
DI Hiérarchique
 Le système d’injection de dépendance d’Angular est hiérarchique
 Il possède deux hiérarchie d’injecteur
 Une hiérarchie d’injecteur niveau composant (element Injector
Hierarchy)
 Une hiérarchie d’injecteur niveau module.

 La hiérarchie composant (appelé aussi Node Injector Hierarchy)


est la plus prioritaire

239
DI Hiérarchique
Hiérarchie d’injecteur niveau composant
Le système d’injection de dépendance d’Angular est hiérarchique.
Un arbre d’injecteur est crée. Cet arbre est // à l’arbre de composant.
L’algorithme suivant permet la détection de l’injecteur adéquat :

Il l’injecte

ok
Injection d’un Vérification de
service dans un l’injecteur à ce
Tant que il ne le
composant niveau
Vérifie dans le trouve pas et qu’on est
père pas arrivé dans la
racine

240
DI Hiérarchique
Hiérarchie d’injecteur niveau Module
 Si Angular ne trouve pas de provider au niveau de la hiérarchie des
composants, il va aller voir dans la hiérarchie de module.
 Le ModuleInjector peut être configuré de deux manières en utilisant :
La propriété @Injectable pour référencer un NgModule, ou ‘root’
 Le tableau des providers dans @NgModule()
 Le moduleInjector identifie les providers disponibles en effectuant
un aplatissement de tous les tableaux de fournisseurs qui peuvent
être atteints en suivant les [Link] de manière récursive.

241
242
Exercice
Ajouter les services suivants afin d’améliorer la gestion de notre
plateforme d’embauche.

Un premier service CvService qui gérera les Cvs. Pour le moment
c’est lui qui contiendra la liste des cvs que nous avons.
Ajouter aussi un composant pour afficher la liste des cvs embauchées
ainsi qu’un service EmbaucheService qui gérer les embauches.
Au click sur le bouton embaucher d’un Cv, le cv est ajouté à la liste
des personnes embauchées et une liste des embauchées apparait.

243
Exercice

244
Exercice

245
DI Hiérarchique
Configurer le Mechanism de DI en utilisant
les Resolution Modifier
 Le mécanisme de DI d’Angular est configurable
 Il existe un ensemble de décorateurs qui vous permettent d’adapter le
comportement par défaut.
 @Optional
 @SkipSelf
 @Self
 @Host

246
DI Hiérarchique
Configurer le Mechanism de DI en utilisant
les Résolution Modifier : @Optional
 @Optional indique qu'une dépendance peut être absente.
 Ceci va dire à Angular de retourner null s’il ne trouve pas la
dépendance au lieu de déclencher une erreur.
 Ceci est utile lorsque vous voulez permettre à un composant de
fonctionner même si une dépendance spécifique n'est pas disponible.
 Afin de prévenir ca, ajouter le décorateur @Optional devant l’injection.
 Cependant, il faut gérer le cas ou l’injection ne se fait pas.

247
DI Hiérarchique
Configurer le Mechanism de DI en utilisant
les Résolution Modifier : @Optional

constructor(
@Optional()private logger: LoggerService
){
if ([Link]) {
[Link]('logged From logger service :D');
} else {
[Link]('Logged from the external console log')
}
}

248
DI Hiérarchique
Configurer le Mechanism de DI en utilisant
les Résolution Modifier : @Self
 Si vous voulez considérer uniquement que le provider qui est
fourni au niveau du composant et pas dans la hiérarchie de DI,
vous pouvez utiliser le décorateur @Self.
constructor(
@Optional() @Self() private logger: LoggerService
){
if ([Link]) {
[Link]('logged From logger service :D');
} else {
[Link](
`No provider in my Providers array,
So I’am Logged from the external console log`
)
}
} 249
DI Hiérarchique
Configurer le Mechanism de DI en utilisant
les Résolution Modifier : @SkipSelf
 Par défaut la hiérarchie de DI commence par chercher un provider
au niveau du Composant lui-même.
 Imaginez que vous voulez provider un service pour vos fils, mais que
vous voulez récupérer une instance dans la hiérarchie qui commence par
le composant parent.
 Le décorateur @SkipSelf vous permet de le faire.

250
DI Hiérarchique
Configurer le Mechanism de DI en utilisant
les Résolution Modifier : @Host
 Le décorateur @Host arrête la recherche au niveau du composant hôte.
Ce qui ressemble à @Self mais avec certaines spécificités.
 Le composant hôte est généralement le composant demandant la
dépendance.
 Cependant, lorsque ce composant est projeté ( avec ng-content ) dans un
composant parent, ce composant parent devient l'hôte et il est utilisé en
second lieu, donc si l’instance ne se trouve pas dans le composant lui-
même on cherche dans le composant dans lequel il est projeté.
 Si vous faite une injection dans une directive, le composant appelant la
directive devient l’hôte. Cependant il faut utiliser la clé viewProviders

251
Les services Angular
 Angular vous fournit certains service prêt à l’empoi et vous permettant
certaines fonctionnalités.
 Le Core Angular vous fournit 2 services très utiles :
 Title
 Meta

252
Les services Angular
Title
 Le service Title vous permet de manipuler la balise title de la page
active.
 Angular étant une SPA, vous ne pouvez pas modifier directement la
balise title dans votre ‘page’
 Pour se faire injecter le service Title dans votre page component
 Ce service vous offre deux méthode :
 setTitle pour modifier le titre
 getTitle pour récupérer le titre

253
[Link]({
Les services Angular name: 'author', content: 'aymen'
Meta });

 Le service Meta vous permet de manipuler les balises type MetaDefinition = {


meta de la page active. charset?: string;
content?: string;
 Ces balises servent à injecter des méta données sur httpEquiv?: string;
votre page et permette donc un meilleur référencement. id?: string;
itemprop?: string;
Pour se faire injecter le service Meta dans votre page name?: string;
property?: string;
component scheme?: string;
 Ce service vous offre plusieurs méthodes : url?: string;
}
 addTag pour ajouter un tag
 addTags pour ajouter un tableau de tag
 getTag pour récupérer un tag d’un attribut
 getTags pour récupérer des tag d’un attribut …
254
Exercice
 Testez les deux services

255
Angular
Routing
AYMEN SELLAOUTI

256
Objectifs
1. Définir le routeur d’Angular
2. Définir une route
3. Déclencher une route à partir d’un composant
4. Ajouter des paramètres à une route
5. Récupérer les paramètres d’une route à partir du composant.
6. Préfixer un ensemble de routes
7. Gérer les routes inexistantes

257
Qu’est ce que le routing
Tout système de routing permet d’associer une route à un traitement
Angular SPA. Pourquoi parle-on de route ??
Séparer différentes fonctionnalités du système
Maintenir l’état de l’application
Ajouter des règles de protection
Que risque t-on d’avoir si on n’utilise pas un système de routing ?

On ne peut plus rafraichir notre page


Plus de Favoris 
Comment partager vos pages ????
258
Création d’un système de Routing
 Afin de créer un système de routing, il suffit de provider vos route
avec la fonction provideRouter au niveau de la fonction
bootstrapApplication

bootstrapApplication(AppComponent, appConfig) export const routes: Routes = [


.catch((err) => [Link](err)); { path: '', component: HomeComponent },
]
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes)
]
}
259
Préparer l’emplacement d’affichage des
vues correspondantes aux routes
 Pour indiquer à Angular ou est ce qu’il doit charger les vues spécifiques
aux routes nous utilisons le router outlet.

 Router outlet est une directive qui permet de spécifier l’endroit ou la


vue va être chargée.

 Sa syntaxe est <router-outlet></router-outlet>

260
Préparer l’emplacement d’affichage des
vues correspondantes aux routes
@Component({ Header
selector: "app-root", <hr>
standalone: true, <router-outlet />
imports: [RouterOutlet], <hr>
templateUrl: "./[Link]", Footer
styleUrl: "./[Link]", HTML
})
export class AppComponent {
title = "ng18";
}
TS

261
Syntaxe minimaliste d’une route
 Une route est un objet.

 Les deux propriétés essentielles sont path et component.

 path permet de spécifier l’URI. Cette url ne doit pas commencer par un /

 component permet de spécifier le composant à exécuter.


{path: '',component:CvComponent},
{path:'onlyHeader',component:HeaderComponent}

262
Exercice
 Configurer votre routing

 Créer deux composants

 Créer deux routes qui pointent sur ces deux composants

 Vérifier le fonctionnement de votre routing

263
Déclencher une route routerLink
L’idée intuitive pour déclencher une route est d’utiliser la balise a et
son attribut href. Est-ce que ca risque de poser un problème ?
L’utilisation de <a href > va déclencher le chargement de la page ce
qui est inconcevable pour une SPA.
La solution proposée par le router d’Angular est l’utilisation de la directive
routerLink qui comme son nom l’indique liera la directive à la route que
nous souhaitons déclencher sans recharger la page.
Exemple :
<li ><a [routerLink]="[‘todo’]" routerLinkActive="active">Gérer les
cvs</a></li>

264
Déclencher une route routerLink
 routerLinkActive="active" va associer la classe active à l’uri cible ainisi qu’à
tous ces ses ancetres.
Par exemple si on a l’uri ‘cv/liste’ la classe active sera ajouté à cet uri ainsi
qu’à l’uri ‘cv’ et ‘’.
Pour identifier uniquement l’uri cible, ajouter la directive suivante :
[routerLinkActiveOptions]="{exact: true}”

265
Exercice
 Faites en sorte d’avoir un composant Header dans votre application qui permet
d’afficher l’ensemble de vos liens.
 En cliquant sur un lien, le composant qui lui est associé doit être affiché.

266
Déclencher une route à partir du
composant
 Afin de déclencher une route à travers le composant on utilise le service
Router et sa méthode navigate.

 Cette méthode prend le même paramètre que le routerLink, à savoir un


tableau contenant la description de la route.

 Afin d’utiliser le Router, il faut l’importer de l’@angular/router et


l’injecter dans votre composant.

267
Déclencher une route à partir du
composant
import { Component} from '@angular/core';
import {Router} from "@angular/router";
@Component({
selector: 'app-home',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class HomeComponent{
constructor(private router:Router) { }
onNaviger(){
[Link](['/about/10']);
}

268
Le service Router
 Le service Router d'Angular fournit des fonctionnalités de
navigation de route pour votre application.
 Il permet de naviguer vers des routes définies en utilisant la méthode
navigate() ou navigateByUrl(), de s'abonner aux événements de
navigation en utilisant la propriété events, et de récupérer des
informations sur la route active en utilisant la propriété url ou la
propriété routerState.

269
Exercice
 Créer un composant appelé RouerSimulator

 Dans ce composant créer une liste déroulante contenat le nom des différentes
routes de votre application.

 Ajouter ce composant au même niveau que le header et que le <router-outlet>.

 En sélectionnant le nom d’un composant, il doit apparaitre dans le


<router-outlet> simulant ainsi le fonctionnement d’un routeur.

270
Exercice : RouterSimulator

271
Les paramètres d’une route
 Afin de spécifier à notre router qu’un segment d’une route est un
paramètre, il suffit d’y ajouter ‘:’ devant le nom de ce segment.

 Exemple
 /cv/:id permet de dire que la root contient au début cv ensuite un
paramètre de root appelé id.

272
Récupérer les paramètres d’une route
ActivatedRoute
 Afin de récupérer les paramètres d’une route au niveau d’un composant,
Angular nous fournit un service qui gère la route active, c’est le
ActivatedRoute
 L'objet ActivatedRoute dans Angular est un objet qui contient des
informations sur la route actuellement activée.
 Il est généralement utilisé pour accéder aux informations de route
telles que les paramètres de route, les données de route et les
paramètres de requête.
 Il est également utilisé pour souscrire aux changements de route.

273
Récupérer les paramètres d’une route
ActivatedRoute
 params: Retourne un observable des paramètres de route actuels.
 queryParams: Retourne un observable des paramètres de requête
actuels.
 data: Retourne un observable des données de route associées à la
route actuelle.
 snapshot: Retourne un instantané de la route actuelle.
 url: Retourne un observable de l'URL de la route actuelle.
 parent: Retourne l'instance de ActivatedRoute de la route parente.
 firstChild: Retourne l'instance de ActivatedRoute du premier enfant
de la route actuelle.

274
Récupérer les paramètres d’une route
ActivatedRoute
 children: Retourne un tableau d'instances de ActivatedRoute des
enfants de la route actuelle.
 paramMap: Retourne un observable qui contient les paramètres de
route sous forme de map.
 queryParamMap: Retourne un observable qui contient les
paramètres de requête sous forme de map.

275
Récupérer les paramètres d’une route
ActivatedRoute

276
Récupérer les paramètres d’une route
ActivatedRoute / snapshot
 La propriété snapshot de l'objet ActivatedRoute contient un instantané de
l'état de la route actuelle.
 Elle contient des informations telles que l'URL actuelle, les paramètres
de route actuels,…
 Elle est généralement utilisée pour accéder aux informations de route
dans un composant lors de son initialisation.
 Il est important de noter que l'instantané de la route ne change pas
lorsque la route change. Il représente un état figé de la route lors de son
instanciation.

277
Récupérer les paramètres d’une route
ActivatedRoute / snapshot
 Voici quelques propriétés courantes de l'API snapshot :
 url: Retourne l'URL de la route actuelle sous forme de tableau de
segments d'URL.
 params: Retourne un objet qui contient les paramètres de route actuels.
 queryParams: Retourne un objet qui contient les paramètres de requête
actuels.
 fragment: Retourne la partie de l'URL après le symbole "#".
 data: Retourne les données de route associées à la route actuelle.
 outlet: Retourne le nom de l'outlet de route actuel.
 component: Retourne le composant de route actuel.
 routeConfig: Retourne la configuration de la route actuelle.
278
Récupérer les paramètres d’une route
ActivatedRoute / snapshot

279
Récupérer les paramètres d’une route
ActivatedRoute / snapshot
 Donc pour accéder à votre propriété, passez par l’objet snapshot
 Avec snapshot, vous avez deux méthodes pour récupérer les
paramètres:
 Via la propriété params qui retourne un tableau d’objet des paramètres
 Via la propriété paramMap
 Appeler sa méthode get
 Passez lui le nom de la propriété souhaitée.

[Link]('id')

280
Exercice
 Reprendre le composant qui permet
de changer la couleur de la DIV

 Ajouter lui une route

 Faite en sorte que cette route soit de


cette forme /color/:couleur et qui
permettra d’affecter la couleur
récupérée par la route comme couleur
par défaut du DIV.

281
Passer le paramètre à travers le tableau de
routerLink
 Une autre méthode permet de passer le paramètre de la route est en
l’ajoutant comme un autre attribut du tableau associé au routerLink
import { Component, OnInit } from '@angular/core';
import {Router} from "@angular/router";
@Component({
selector: 'app-home',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class HomeComponent{
constructor(private router:Router) { }
id:number=10;
onNaviger(){[Link](['/about',[Link]]);}
}
282
Les queryParameters
 Les queryParameters sont les paramètres envoyé à travers une
requête GET.
 Identifié avec le ?.
 Afin d’insérer un queryParameters on dispose de deux méthodes
 On ajoute dans la méthode navigate du Router un second
paramètre de type objet.
 L’une des propriétés de cet objet est aussi un objet dont la clé est
queryParams dont le contenu est aussi un objet content les
identifiants des queryParams et leurs valeurs.

[Link](['/about',[Link]],{queryParams:{'qpVar':'je suis un qp'}});


283
Les queryParameters
 La deuxième méthode est en l’intégrant à notre routerLink de la manière
suivante :

<a [routerLink]="['/about/10']" [queryParams]="{qpVar:'je suis


un qp bindé avec le routerLink'}">About</a>

284
Récupérer Les queryParameters
 Les queryParameters sont récupérable de la même façon que les
paramètres. Soit d’une façon statique avec snapshot via la
propriété queryParams ou sa propriété queryParamMap et sa
méthode get.
 Soit dynamiquement via l’observable queryParams

[Link]('id')

285
Route Fils
 Certains composants ne sont visible qu’à l’intérieur d’autres
composants.

 Prenons l’exemple d’un objet Personne. En accédant à la route


/personne/:id nous avons l’affichage de la personne et nous
aimerions avoir deux boutons. Un pour éditer la Personne (route
/personne/:id/editer). L’autre pour afficher ces détails (route
/personne/:id/apercu).

 L’idée est de préfixer nos routes.


286
Route Fils
 Afin de mettre en place ce processus nous procédons comme suit :

 Nous définissons le préfixe avec la propriété path.

 Nous y ajoutons la propriété children qui contiendra le tableau


des routes. Chaque route de ce tableau sera préfixé avec la route
définie dans path.

287
Route Fils
const CV_ROUTE: Routes = [
{
path: 'cv',
children: [
{path: '', component: CvComponent },
{path: 'detail/:id', component: DetailCvComponent },
{path: 'addPersonne', component: FormPersonneComponent },
]
}
];

288
Exercice
 Modéliser un système de routage qui utilise ses propriétés.

289
Route fils / définition dans un parent
 Supposons que nous voulons avoir un Template central avec des
données fixe et des parties variables dans le même template.

 En changeant les routes, le contenu principal doit rester le même et la


partie variable doit changer selon la route.

290
Route Fils
 Afin de mettre en place ce processus nous procédons comme suit :
 Nous définissons le préfixe avec la propriété path. On lui associe
le composant Père.
 Nous y ajoutons la propriété children qui contiendra le tableau
des routes. Chaque route de ce tableau sera préfixé avec la route
définie dans path.
 Nous ajoutons la balise <router-outlet></router-outlet> dans le
Template père.

291
Route Fils

const CV_ROUTE: Routes = [


{
path: 'cv',
component: CvComponent,
children: [
{path: 'detail/:id', component: DetailCvComponent },
{path: 'addPersonne', component: FormPersonneComponent },
]
}
];

292
Master Detail Implementation
 Le pattern "master-detail" dans Angular est un modèle
d'architecture utilisé pour afficher des données dans une interface
utilisateur.
 Il consiste à avoir une vue "maître" qui affiche une liste
d'éléments, et une vue "détail" qui affiche les détails d'un
élément sélectionné dans la vue "maître".
 Cela permet aux utilisateurs de naviguer facilement entre les
différents éléments et de voir les détails correspondants sans avoir à
charger une nouvelle page.

293
Exercice
Master Detail Implementation
 Nous voulons implémenter le pattern Master Details pour notre
liste de cvs.
 Créer un nouveau composant MasterDetailsCv.
 Il devra permettre l’affichage de la liste des cvs. Au click un détail du
cv sélectionné devra apparaitre.

294
Exercice
Master Detail Implementation

295
Redirection
 Afin de rediriger une route il suffit d’ajouter une propriété dans
l’objet route qui est redirectTo. Cette propriété permet d’indiquer
vers quelle route le path doit être redirigé. Si la route n’a pas encore
été matché, alors les routes commençant par ce path seront
redirigées.
 Une autre propriété peut être utilisé qui est la propriété pathMatch.
Cette propriété permet de définir comment le matching des path est
exécuté. Avec la valeur ‘full’, elle spécifie au routeur de ne faire la
redirection que si le path exact est matché.

296
Redirection : exemple

const APP_Routes:Routes =[
{path:'',component:HomeComponent},
{path:'about',redirectTo:'', pathMatch:'full'},
{path:'about/:param',component:AboutComponent},
{path:'about/:param',component:AboutComponent,children:FILS_ROUTE},
]

297
Redirection : gestion d’erreurs de rooting
 Afin de rediriger une route inexistante vers une page d’erreur, il
suffit de garder la même syntaxe de redirection et de mettre dans la
propriété path ’**’.

298
Exemple
const APP_ROUTE: Routes = [
{path: '', redirectTo: 'cv', pathMatch: 'full'},
{path: ‘cv', component: CvComponent},
{path: 'lampe', component: ColorComponent},
{path: 'login', component: LoginComponent},
{path: 'error', component: ErrorPageComponent},
{path: '**', component: ErrorPageComponent }
];

299
Exercice
 Ajouter les fonctionnalités suivante à votre cvTech:
 Une page détail qui va afficher les détails d’un cv.
 Un bouton dans chaque cv qui au click vous envoi vers la page
détails.
 Dans la page détail, un bouton delete qui au click supprime ce cv et
vous renvoi à la liste des cvs.

300
Router Resolver
 Analysons la page DetailsCv

301
Router Resolver
 Le Router Resolver d'Angular est un mécanisme qui permet de
résoudre des données avant qu'une route ne soit chargée.
 Il permet de charger les données nécessaires pour afficher une
vue spécifique en utilisant un service qui est lié à la route.
Il est utilisé lorsque vous avez besoin de charger des données
avant d'afficher une vue, comme pour afficher des données d'un
utilisateur avant d'afficher sa page de profil.
Il est également utilisé pour éviter les erreurs de chargement de
vue en garantissant que les données nécessaires sont chargées
avant de naviguer vers une route.

302
Router Resolver
 Le Router Resolver est soit une fonction (à partir d’Angular 15)
soit une classe qui implémente l’interface Resolve et qui est
générique. Vous devez donc spécifier ce que le Resolver va fournir
comme données.
 Vous devez implémenter la fonction Resolve qui devra retourner
un objet de T ou une Promise<T> ou un Observable<T>.
Elle prend en paramètre un objet de type ActivatedRouteSnapshot
qui vous permet de récupérer les paramètre de votre route à
travers le paramètre paramMap et sa méthode get.

303
Router Resolver

import { ResolveFn } from '@angular/router';

export const firstResolver: ResolveFn<boolean> = (route, state) => {


return true;
};

304
Router Resolver
@Injectable({
providedIn: 'root',
})
export class CvResolver implements Resolve<Cv> {
resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<Cv> | Cv {
const cvId = [Link]('id');
}
} 305
Router Resolver
 Maintenant, afin de passez le résultat de votre resolver à votre
route, ajoutez une propriété resolve à votre route dans votre fichier
de routing et passer lui un object avec comme clé le nom que vous
voulez donner à votre propriété et comme valeur le resolver.
{
path: ':id',
component: DetailsCvComponent,
resolve: {
cv: CvResolver
}
},
306
Router Resolver
 Maintenant, dans votre composant, injecter le service
ActivatedRoute.
 Pour accéder à la valeur de retour du resolver (si c’est une Promise
ou un Observable, il attendra la valeur émise), accéder à la propriété
snapshot qui contient une propriété data qui contiendra le
champ.
 Si vous voulez un accès dynamique, utilisez l’Observable data de
l’ActivatedRoute.

[Link]['cv'];
307
Exercice
 Appliquez le resolver pour les composants detailsComponent et
CvComponent

308
Route Provider
A partir d’Angular 14
 A partir d’Angular 14, la clé provider a été introduite dans l’objet
route.
 Ceci permettra de provider des provider pour la route et ses enfants
{
path: 'routerProvider',
component: RouterPoviderComponent,
providers: [
{
provide: LoggerService,
useExisting: NewLoggerService
}
]
},
309
Route Provider
A partir d’Angular 14
 Ceci va créer un nouvel Injector qui sera appelé juste après l’element
Injector

{
path: 'routerProvider',
component: RouterPoviderComponent,
providers: [ LoggerService ]
},

[Link] 310
[Link] 311
Le cycle de vie du routeur
Pour chaque navigation, une série d'étapes a lieu avant que le routeur
n'affiche les nouveaux composants à l'écran. C'est ce qu'on appelle le cycle de
vie de navigation du routeur.
Le résultat d'une navigation réussie est que les nouveaux composants seront
rendus à l'aide de <router-outlet>, et une arborescence de structures de données
ActivatedRoute sera créée en tant qu'enregistrement interrogeable de la
navigation.
 Durant ce cycle de vie, des events vous permettent de vous y greffer.
 Vous pouvez activer la visualisation de ces events en développement via l’option
enableTracing [Link](ROUTES, {
enableTracing: true
})
[Link] 312
Router Events
Le routeur d’Angular déclenche plusieurs événements vous permettant
de vous y greffer et d’y ajouter un comportement métier.
 Afin de vous y greffer, injecter le Service Router.
 Il contient un Observable events qui retourne l’event déclenché.
 Inscrivez vous à l’observable et selon le type de l’event voulu, exécutez
le traitement. [Link]((event) => {
if (event instanceof NavigationStart) {
[Link]('Navigation Start:D ', event);
} else if (event instanceof NavigationEnd) {
[Link]('Navigation END :( ', event);
}
}); 313
Router Events
 Les événements du routeur Angular incluent:
 NavigationStart: déclenché lorsqu'une nouvelle navigation est démarrée. Il
peut être utilisé pour déclencher un loader.
 RoutesRecognized: déclenché lorsque le routeur a reconnu les routes
correspondant à l'URL demandée.
 NavigationEnd: déclenché lorsque la navigation a abouti avec succès. Peut
être utilisé pour arrêter le loader ou envoyer une notification à votre outil
Analytics.
 NavigationCancel: déclenché lorsque la navigation est annulée. Peut être
utilisé pour arrêter le loader
 NavigationError: déclenché lorsqu'une erreur survient lors de la navigation.
Peut être utilisé pour arrêter le loader ou envoyer des log au serveurs.
314
Router Events
GuardsCheckStart: déclenché lorsque le routeur démarre la
vérification des gardes de route.
ChildActivationStart: déclenché lorsque l'activation d'un
composant enfant démarre.
ActivationStart: déclenché lorsque l'activation d'un composant
démarre.
GuardsCheckEnd: déclenché lorsque le routeur a terminé la
vérification des gardes de route.
ResolveStart: déclenché lorsque le routeur démarre la résolution des
données pour une route..

315
Router Events
ResolveEnd: déclenché lorsque le routeur a terminé la résolution des
données pour une route.
ActivationEnd: déclenché lorsque l'activation d'un composant est
terminée.
ChildActivationEnd: déclenché lorsque l'activation d'un composant
enfant est terminée.

316
Exercice
 Utiliser la bibliothèque ngx-ui-loader afin de créer un loader qui se déclenche
pour les transitions de vos routes.

317
Router
Ajouter des données à votre Route
 Nous avons vu que nous pouvons ajouter des données à votre route à
travers le resolver.
 Ceci n’est pas l’unique manière de le faire.
 Le resolver sert lorsque les données sont dynamique, cependant
lorsque les données que vous voulez passez à votre route sont statique,
vous pouvez passez par la propriété data de votre route.
Ceci peut être utile lorsque le composant peut être utilisé dans deux
contextes différents {
path: 'cv',
component: CvComponent,
data: {
blackListName: ['Sellaouti']
}
},
318
Récupérer les paramètres d’une route
ActivatedRoute / L’observable params
 Si vous rappeler votre composant avec la même route mais en
changeant les paramètres, Angular ne recrée par une nouvelle
instance du composant, ceci implique que ni le constructeur, ni le
ngOnInit ne soient appelés.
 Afin de récupérer les paramètres d’une root au niveau d’un composant
d’une façon dynamique on doit utiliser la propriété params qui est un
Observable.
 Affecter le paramètre à une variable du composant en s’inscrivant avec la
méthode subscribe à l’observable params de notre ActivatedRoute.
Cette variable retourne un tableau de l’ensemble des paramètres.
[Link](params=>{[Link]=params['param']});
319
Récupérer les paramètres d’une route
import {RouterModule, Routes} from
'@angular/router'; import { Component, OnInit } from '@angular/core';
import {HomeComponent} from import {ActivatedRoute} from '@angular/router';
"./app/home/[Link]"; @Component({ 1
import {AboutComponent} from selector: 'app-about',
"./app/about/[Link]"; templateUrl: './[Link]',
styleUrls: ['./[Link]']
const APP_Routes:Routes =[
{path:'',component:HomeComponent},
})
export class AboutComponent {
{path:'about/:param',component:AboutCo
mponent}, monParam:any; 2
] 4 constructor(private router:ActivatedRoute) {
; [Link](params=>{[Link]=par
ams['param']});
export const routing =
[Link](APP_Routes);
} 3
}

[Link]

320
Les navigationExtras
 A partir de la version 7.2 d’Angular, on peut passer des
informations via le routeur d’une façon dynamique en utilisant les
navigationExtras.
 Une première façon de passer ces informations est à travers la
méthode navigate de votre router.
 En effet, le second paramètre de cette méthode est un objet de type
NavigationExtras. Cet objet a une propriété state dans laquelle vous
pouvez passer les informations que vous voulez. [Link](['cv'], {
state: {
id: 5
}
});
321
Les navigationExtras
 Afin de récupérer cet objet dans le composant cible, vous devez
utiliser le router et appelez sa méthode getCurrentNavigation()
 Cette méthode permet de récupérer un Objet de type Navigation qui
possède un objet extras de type NavigationExtras qui lui-même
possède la propriété state contenant les informations envoyées.

const routeState =
[Link]()?.[Link];
if (routeState) {
[Link](routeState['id']);
}

322
Configuration du RouterModule

[Link](
routes, {
scrollPositionRestoration: 'enabled',
paramsInheritanceStrategy: 'always',
malformedUriErrorHandler: (error: URIError, urlSerializer:
UrlSerializer, url: string) => [Link]("/page-not-
found")
})

323
Angular
Form
AYMEN SELLAOUTI

324
Plan du Cours
1. Introduction
2. Les composants
3. Les directives
3 Bis. Les pipes
4. Service et injection de dépendances
5. Le routage
6. Form
7. HTTP
8. Les modules
9. Les tests unitaires
325
Approche de gestion de FORM
1. Approche basée Template

2. Approche réactive

326
Objectifs
1. Créer un formulaire
2. Ajouter des validateurs
3. Appréhender les classes Css générées par le formulaire
4. Manipuler l‘objet ngForm
5. Manipuler les controles du formuliare

327
Approche basée Template/ Template
Driven Approach
1 Importer le module FormsModule dans [Link]

2 Angular détecte automatiquement un objet form à l’aide de la balise FORM. Cependant, il ne


détecte aucun des éléments (inputs).
<input
type="text"
3 Spécifier à Angular quel sont les éléments (contrôles) à gérer. id="username"
- Pour chaque élément ajouter la directive angular ngModel. class="form-
- Identifier l’élément avec un nom permettant de le détecter control"
et de l’identifier dans le composant. ngModel
4 Associer l’objet représentant le formulaire à une variable et la passer à votre name="username"
fonction en utilisant le référencement interne # et la directive ngForm >

328
Angular Form
Form control

329
Approche basée Template/ Template
Driven Approach

export class
TmeplateDrivenComponent{
<form
onSubmit(formulaire: NgForm){
(ngSubmit)="onSubmit(formulaire)"
#formulaire="ngForm"> [Link](formulaire);
}
}

Template [Link]

330
Approche basée Template
Validation
Afin de valider les propriétés des différents contrôles, Angular utilise des attributs et
des directives

- required
- email

La propriété valid de ngForm permet de vérifier si le formulaire est valid ou non en se


basant sur les validateurs qu’ils contient.
[Link]

331
Approche basée Template
NgForm
En détectant le formulaire, Angular décore les différents éléments du formulaire avec
des classes qui informe sur leur état :
 dirty : informe sur le fait que l’une des propriétés du formulaire a été modifié ou
non

 Valid : informe si le formulaire est valide ou non

 untouched : informe si le formulaire est touché ou non

 pristine : le formulaire n’a pas été touché, c’est l’opposé du dirty

332
Exercice
 Créer un formulaire d’authentification contenant les champs suivants :
 Email
 Password
 Envoyer
 Si un champ est invalide alors il devra avoir une bordure rouge.
 Les deux champs sont obligatoires
 Le password doit avoir au moins 4 caractères.
 Un champ vide et non encore modifié ne peut avoir de bordure rouge que s’il a
été touché.
 Le bouton « envoyer » ne doit être cliquable que si le formulaire est valide.
 Utiliser le binding sur la propriété disabled.

333
Exercice

334
Approche basée Template
Accéder aux propriétés d’un champ (contrôle)
du formulaire
 Pour accéder à l’objet form et ces propriétés nous avons utilisé
#notreForm=«ngForm »

 Pour les champs du formulaire c’est la même chose mais au lieu du


ngForm c’est un ngMdoel
#notreChamp=« ngModel »

335
Exercice
 Ajouter un petit message d’erreur qui devra s’afficher sous le
champs de l’email s’il est invalide. Ce champ ne devra apparaitre que
si l’utilisateur accède ou modifie le champ email.
 Ajouter un champ d’erreur pour signaler l’erreur à l’utilisateur.

336
Approche basée Template
Associer des valeurs par défaut aux champs
 Pour associer des valeurs par défaut aux champs d’un formulaire
associé à Angular il faut le faire à partir du composant.

 Afin de gérer les valeur du formulaire à partir du composant il faut


du binding.

 Au lieu d’avoir juste la primitive ngModel associée au contrôle d’un


élément on ajoute le property binding avec [ngModel]

337
Exercice
 Ajouter la valeur par défaut « myUserName » au champ username.

338
Approche basée Template
ngModelChange et ngModelOptions
 A chaque changement dans vos input, un événement
ngModelChange est déclenché.
 Vous pouvez aussi manipuler quand cet événement est déclenché à
travers le binding de la directive ngModelOptions.
 Elle prend en paramètre un objet contenant les propriétés suivantes :
 updateOn : Définit l'événement sur lequel la valeur de
contrôle du formulaire et la validité sont mises à jour.
 La valeur par défaut est ‘change'.
 Valeurs possibles : `change` | `blur` | `submit`.

339
Approche basée Template
ngModelChange et ngModelOptions
 name : une alternative à la définition de l'attribut name sur
l'élément de contrôle de formulaire.
 standalone : lorsqu'il est défini sur true, le `ngModel` ne
s'enregistrera pas avec son formulaire parent, et agit comme
si ce n'était pas dans le formulaire. La valeur par défaut est
false. Si aucun formulaire parent n'existe, cette option n'a aucun
effet.

340
Approche basée Template
Récap
Difficilement maintenable quand le formulaire grandit
Au fur et à mesure que nous ajoutons de plus en plus de balises de validation à un
champ ou lorsque nous commençons à ajouter des validations inter-champs
complexes, la lisibilité et la maintenabilité du formulaire diminuent.
L'avantage de cette façon de gérer les formulaires est sa simplicité initiale, et
c'est probablement suffisant pour créer des formulaires de petite à moyenne taille.
C'est aussi très similaire à ce qui a été fait dans AngularJs avec ng-model, donc ce
modèle de programmation sera déjà familier à beaucoup de développeurs.
En revanche, la logique de validation du formulaire ne peut pas être
facilement testée et les modèles peuvent devenir complexes assez rapidement.

341
Approche basée Template
Grouping form
 Afin de grouper l’ensemble des contrôles (propriétés/champs) d’un
formulaire, on peut utiliser la technique du « grouping form
controls ».
 Il suffit d’ajouter la directive ngModelGroup dans la div qui
englobe les propriétés à grouper.
<div
ngModelGroup= "user"
#userData= "ngModelGroup"
>

 Afin d’accéder à cet objet vous pouvez le référence localement en


utilisant le mot clé ngModelGroup

342
Exercice
 Grouper les données de votre utilisateur dans un ngModelGroup

 Tester l’objet généré

 Essaye de voir s’il contient les mêmes classes qu’un contrôle simple,
e.g. ng-dirty, ng-valid.

 Ajouter un message d’erreur qui apparait si votre groupe n’est pas


valide.

343
Reactive form
 Les Reactive Form sont une deuxième méthode de gérer vos
formulaires avec Angular.
 Au contraire des Template Driven Form, ses formulaires sont
générés programmatiquement dans la partie TS.
 Ceci permet d’alléger le template des validateurs.
 D’autre part ca permet d’avoir un formulaire testable
 Finalement ceci permet de plus facilement générer des
Validateurs personnalisés.

344
Reactive form
Créer un formulaire
 Commencer par ajouter le module ReactiveFormModule.
 Afin de créer un formulaire avec l’approche réactive, vous devez
créer un objet FormGroup.
 Un FormGroup prend en paramètre un objet décrivant le
formulaire. Chaque champ de l’objet a comme première propriété
le nom et comme valeur un objet définissant les champs associés à
ce formulaire. Ce sont les FormControl.
 Chaque FormControl définit un champ du formulaire. Il prend
en paramètre, la valeur initiale, un Validator ou un tableau de
Validator et en troisième paramètre, un AsyncValidator ou un
tableau d’AsyncValidator
345
Reactive form
Créer un formulaire

ngOnInit() {
[Link] = new FormGroup({
name: new FormControl(null),
firstname: new FormControl(null),
age: new FormControl(null),
});

346
Reactive form
Créer un formulaire

export declare interface AbstractControlOptions {


validators?: ValidatorFn | ValidatorFn[] | null;
asyncValidators?: AsyncValidatorFn | AsyncValidatorFn[] | null;
/**
* @description
* The event name for control to update upon.
*/
updateOn?: 'change' | 'blur' | 'submit';
}
347
Reactive form
Créer un formulaire
form = new FormGroup({
name: new FormControl(null),
firstname: new FormControl("Aymen"),
age: new FormControl(0, {
nonNullable: true,
validators: [Link],
updateOn: "blur",
}),
});

348
Reactive form
Créer un formulaire
 Vous pouvez aussi passer par le service FormBuilder et sa
méthode group.
 Elle prend en paramètre un objet avec comme clé le nom du
formControl et comme valeur un tableau avec comme première
propriété la valeur initiale du champ.
 Ceci est moins verbeux et plus simple à gérer pour les grands
formulaires.
constructor(
[Link] = [Link]({
private formBuilder: FormBuilder
email: [null],
) {}
username: [null],
userCategory: ['employee'],
});
349
Reactive form
Associer le FormGroup à votre form
 Une fois le formulaire définit, nous devons l’associer au form de votre
template.
 Pour ce faire, vous devez ajouter la directive formGroup (qui se
trouve dans le module ReactiveFormsModule) au niveau de la balise
form et la binder à votre objet de type FormGroup au niveau de
votre fichier TS.

350
Reactive form
Associer les FormControl à vos input
 Afin d’associer votre FormControl à votre champ input, utiliser la
directive formControlName et associer le à l’identifiant du
FormControl

351
Reactive form
Récupérer les FormControl dans le HTML
 Afin de récupérer les FormControl dans le HTML, utiliser la
méthode get de votre form group et passer lui l’identifiant du
FormControl à récupérer.
<button
class="btn btn-primary"
[disabled]="[Link]('password').valid"
(click)="process()">
Submit
</button>

352
Reactive form
Récupérer les erreurs du FormControl dans le
HTML
 Afin de récupérer les erreurs de votre control dans le HTML,
utiliser l’attribut errors.

<div
*ngIf="[Link]('name')?.errors && [Link]('name')?.touched"
class="alert alert-danger“
>

353
Reactive form
Récupérer les FormControl dans le HTML
 Une deuxième méthode pour avoir un code moins verbeux est de
créer des getters pour vos champs.

get name(): AbstractControl {


return [Link]("name")!;
}

<div *ngIf="[Link] && [Link]">


Ce champ est obligatoire
</div>
354
Reactive form
Soumettre le formulaire
 A l’inverse de l’approche ‘template driven’, vous n’avez pas
besoin de récupérer la référence de l’objet form puisque vous
l’avez déjà crée dans votre ts.
 Il suffit donc d’écouter le submit avec ngSubmit ou le clic sur un
bouton pour déclencher la méthode du composant qui gérera
l’envoi du formulaire.

355
Exercice

 Reprenez le formulaire de login et gérer le avec les reactive form.

356
Reactive form
Les Validateurs
 Un validateur est une fonction qui retourne false si un champ
est valide selon une certaine condition sinon elle retourne une
information sur l’erreur.
 Afin de valider votre formulaire, passer en deuxième paramètre de
FormControl une référence à une méthode de la classe
Validators ou un tableau de ces méthodes.

357
Reactive form
Les Validateurs
 Afin de valider votre formulaire, passer en deuxième paramètre de
FormControl une référence à une méthode de la classe
Validators ou un tableau de ces méthodes.

358
Reactive form
Les Validateurs
 Validateurs synchrones : Ce sont des fonctions qui contrôle votre
élément et retournent un ensemble d’erreurs de validation ou null.
C’est ce qu’on passe en deuxième paramètre lors de l’instanciation d’un
FormControl.
 Validateurs asynchrones : Ce sont des fonctions asynchrones qui
contrôle votre élément et retournent une Promise ou un Observable
qui émettent un ensemble d’erreurs de validation ou null. C’est ce
qu’on passe en troisième paramètre lors de l’instanciation d’un
FormControl.
 Pour des raisons de performance, Angular commence par les validateurs
synchrones, s’ils passent il déclenche les validateurs asynchrones.
359
Reactive form
Les Validateurs offerts par Angular
 Vous pouvez utiliser des validateurs offerts par angular ou créer vos
propres validateurs.
 Les validateurs de base sont les mêmes que ceux de l’approche
basée Template.

[Link] 360
Reactive form
Les Validateurs offerts par Angular
new FormGroup({
email: new FormControl(null, [[Link], [Link]]),
username: new FormControl(null, [[Link](3)]),
age: new FormControl(null, [
[Link], [Link]('[0-1]?\d{1,2}')
]),
Méthode 1
});
formGroup: FormGroup = new FormGroup({
name: new FormControl(null, {
validators: [[Link], [Link](3)],
asyncValidators: [],
updateOn: "change"
}),
age: new FormControl(null),
Méthode 2
});
[Link] 361
Exercice
 Ajouter dans votre cvTech un composant contenant un formulaire. Ce formulaire
devra vous permettre d’ajouter un utilisateur.
 Ajouter les validateurs nécessaires.
 Après l’ajout fowarder le user vers la liste des cvs.

362
Reactive form
Manipulez les valeurs de votre form
 Afin de mettre à jour la valeur d’un FormControl ou d’un
FormGroup, vous pouvez utiliser la méthode setValue() qui met à
jour la valeur du contrôle de formulaire et valide la structure de la
valeur fournie par rapport à la structure du contrôle.
 Vous pouvez utiliser la méthode patchValue si vous modifiez
uniquement une partie.
[Link]({ name: "Sellaouti", firstname: "Aymen" });

[Link]({firstname: "Aymen" });

[Link] 363
Reactive form
Manipulez les valeurs de votre form
 En deuxième paramètre de ces deux fonctions, vous pouvez passer
un objet d’options avec deux propriétés:
 onlySelf: Angular vérifie l'état de validation du formulaire,
chaque fois qu'il y a un changement de valeur. La validation
commence à partir du contrôle dont la valeur a été modifiée et
se propage au FormGroup de niveau supérieur. Il s'agit du
comportement par défaut. Si vous ne souhaitez pas qu‘Angular
vérifie la validité de l'ensemble du formulaire, chaque fois
que vous modifiez la valeur à l'aide de setValue ou patchValue,
définissez onlySelf à true.

[Link] 364
Reactive form
Manipulez les valeurs de votre form
 En deuxième paramètre de ces deux fonctions, vous pouvez passer
un objet d’options avec deux propriétés:
 emitEvent: Les formes Angular émettent deux événements.
L'un est ValueChanges et l'autre est StatusChanges.
L'événement ValueChanges est émis chaque fois que la valeur du
formulaire est modifiée. L'événement StatusChanges est émis
chaque fois qu'angular calcule l'état de validation du formulaire.
C'est le comportement par défaut Nous pouvons empêcher que
cela se produise, en définissant l'emitEvent à false

[Link] 365
Reactive form
Manipulez les valeurs de votre form
[Link](
{ firstname: "Aymen" },
{
emitEvent: false,
onlySelf: true,
}
);

[Link] 366
Reactive form
Suivre les modifications de vos FormControls
et de vos FormGroup
 FormControl et FormGroup, vous fournissent deux Observables
permettant le suivi des changements de valeur et de status.
 ValueChanges est un événement déclenché par les formulaires
Angular chaque fois que la valeur de FormControl, FormGroup ou
FormArray change. L'observable obtient la dernière valeur du
contrôle. Il nous permet de suivre les modifications apportées à la
valeur en temps réel et d'y répondre. Par exemple, nous pouvons
l'utiliser pour valider la valeur, calculer les champs calculés, ...

[Link] 367
Reactive form
Suivre les modifications de vos FormControls
et de vos FormGroup
 statusChanges est un événement déclenché par les formulaires
Angular chaque fois que Angular calcule le statut de validation
de FormControl, FormGroup ou FormArray. Il renvoie un
observable afin que vous puissiez vous y abonner. L'observable
obtient le dernier état du contrôle.

[Link] 368
Exercice
 Afin de protéger les informations personnelles des mineurs, faites en
sortes que lorsque l’age de la personne possédant le Cv est inférieur
à 18 ans, il ne puisse pas renseigner le path de l’image.

369
Exercice
 Etant donné que notre formulaire est assez volumineux et pour
permettre une meilleure expérience utilisateur, nous voulons faire en
sorte que si l’utilisateur saisisse un formulaire valide et qu’il sorte de
la page sans l’envoyer, il puisse le retrouver rempli lorsqu’il retourne
à la page d’ajout d’un cv.

370
Reactive form
Customiser vos validateurs
 Un custom validator est une fonction de type ValidatorFn qui prend en
paramètre un control de type AbstractControl (qui contient une
propriété value représentant la valeur du champ à valider) et retourne
null si la valeur est valide ou un objet de type ValidationErrors s’il y a
des erreurs de validation.
export declare interface ValidatorFn {
(control: AbstractControl): ValidationErrors | null;
}
export declare type ValidationErrors = {
[key: string]: any;
};
371
Reactive form
Customiser vos validateurs
Le Validator, renvoyée par la fonction de création de validateur, doit
suivre ces règles :
Un seul argument d'entrée est attendu, qui est de type
AbstractControl. La fonction validateur peut obtenir la valeur à
valider via la propriété [Link]
La fonction de validation doit renvoyer null si aucune erreur n'a été
trouvée dans la valeur du champ, ce qui signifie que la valeur est valide
Si des erreurs de validation sont trouvées, la fonction doit renvoyer
un objet de type ValidationErrors.

372
Reactive form
Customiser vos validateurs
L'objet ValidationErrors peut avoir comme propriétés les multiples
erreurs trouvées (généralement une seule) et comme valeurs les détails
de chaque erreur.
Si nous voulons simplement indiquer qu'une erreur s'est produite, sans
fournir plus de détails, nous pouvons simplement renvoyer true
comme valeur d'une propriété error dans l'objet ValidationErrors.

return !passwordValid ? {passwordStrength: true} : null;

373
Reactive form
Customiser vos validateurs
export function createPasswordStrengthValidator(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const value = [Link];
if (!value) {
return null;
}
const hasUpperCase = /[A-Z]+/.test(value);
const hasLowerCase = /[a-z]+/.test(value);
const hasNumeric = /[0-9]+/.test(value);
const passwordValid = hasUpperCase && hasLowerCase && hasNumeric;
return !passwordValid ? {passwordStrength: true} : null;
};
}

374
Reactive form
Customiser vos validateurs
Validateurs Asynchrones
Dans certains cas d’utilisation, la validation de votre champ ne se fait
pas d’une façon asynchrone.
Le cas le plus répandu est la validation par votre backend.
Imaginez que vous avez un champ email et qu’il ne doit pas être
redondant. La solution et d’avoir une API qui vérifie ca et qui vous
retourne la réponse.
Ce traitement étant Asynchrone, le Validateur synchrone ne fait plus
affaire.
Il faut donc créer un validateur ASYNCHRONE.

375
Reactive form
Customiser vos validateurs
Validateurs Asynchrones
Le Validator, renvoyée par la fonction de création de validateur, doit
suivre ces règles :
Un seul argument d'entrée est attendu, qui est de type
AbstractControl. La fonction validateur peut obtenir la valeur à valider
via la propriété [Link]
La fonction de validation doit renvoyer une Promise<null>, un
Observable<null> si aucune erreur n'a été trouvée dans la valeur du
champ, ce qui signifie que la valeur est valide
Si des erreurs de validation sont trouvées, la fonction doit renvoyer
une Promise ou un Observable d’un objet de type ValidationErrors.

376
Reactive form
Customiser vos validateurs
Validateurs Asynchrones

export function userExistsValidator(authService: AuthService): AsyncValidatorFn {


return (control: AbstractControl) => {
return [Link]([Link]).pipe(
first()
);
};

377
Reactive form
Customiser vos validateurs
Validateurs Asynchrones
findUserByEmail(value: any): Observable<ValidationErrors | null> {
return new Observable<ValidationErrors | null>(
(observer) => {
setTimeout(
() => {
const date = new Date();
if ([Link]() % 2) {
[Link](null);
} else {
[Link]({userExists: true});
}
}, 1500);
}
);
}
}
378
Exercice
 Le champ Cin étant unique, créer un validateur asynchrone permettant
de gérer cette contrainte et tester le.

379
Reactive form
Customiser vos validateurs
Valider un form
Afin de valider un form, vous devez faire la même chose que pour le
validateur d’un FormControl sauf que dans votre fonction, vous devez
prendre en premier paramètre un objet de type FormGroup

export function creatDateRangeValidator(): ValidatorFn {


return (form: FormGroup): ValidationErrors | null => {
const start: Date = [Link]('startAt').value;
const end: Date = [Link]('endAt').value;
if (start && end) {
const isRangeValid = ([Link]() - [Link]() > 0);
return isRangeValid ? null : {dateRange: true};
}
return null;
};
}
380
Reactive form
Customiser vos validateurs
Valider un form
 Maintenant et pour l’appliquer à votre FormGroup ajouter à la
création de votre FormGroup un second paramètre qui est un objet
options et utiliser la propriété validators
[Link] = new FormGroup(
{},
{
validators: VotreValidateur
}
);
[Link] = [Link](
{},
{
validators: VotreValidateur
}
);

381
Exercice
 Le champ Cin étant composé de 8 caractères numériques, il présente
une corrélation entre l’age de la personne et les deux premiers
caractères.
 Si la personne a un age >= 60 ans, les deux première caractères
numériques doivent être entre 00 et 19.
 Sinon ca doit être supérieur à 19.
 Créer le validateur permettant de faire ca.

382
Reactive form
Imbriquer des formulaires
La partie TS
 Afin d’imbriquer des formulaires, au lieu de passer un FormControl à
votre propriété, passez un objet de type FormGroup.
 De même si vous utilisez le service FormBuilder, pour votre formulaire
imbriqué, utilisez un group.
[Link] = [Link]({
credentials: [Link]({
email: [null, {validators: [[Link], [Link]]}],
password: [null, {validators: [[Link], createPasswordStrengthValidator()]}],
}),
username: [null, {validators: [[Link]]}],
age: [null, {validators: [[Link], [Link]('[0-1]?\d{1,2}')]}],
});

383
Reactive form
Imbriquer des formulaires
La partie TS
 Afin d’imbriquer des formulaires, au lieu de passer un FormControl à
votre propriété, passez un objet de type FormGroup.
 De même si vous utilisez le service FormBuilder, pour votre formulaire
imbriqué, utilisez un group.
[Link] = new FormGroup({
credentials: new FormGroup({
email: new FormControl(null, [[Link], [Link]]),
password: new FormControl(null, [[Link], createPasswordStrengthValidator]),
}),
username: new FormControl(null, [[Link]]),
age: new FormControl(null, [[Link], [Link]('[0-1]?\d{1,2}')]),
});

384
Reactive form
Imbriquer des formulaires
La partie HTML
 Dans la partie HTML, grouper tous les éléments de votre
formulaire imbriqué dans un container (div par exemple)
 Ajouter la propriété formGroupName avec l’identifiant de votre
formGroup.
<div formGroupName="credentials">
<div>
<label for="email">Email</label>
<input class="form-control" type="email" id="email“ formControlName="email“ />
</div>
<div>
<label for="username">Password</label>
<input class="form-control" type="text" id="password“ formControlName="password"/>
</div>
</div> 385
Reactive form
Imbriquer des formulaires
Accéder aux FormControl
 Pour accéder aux FormControl des formulaires imbriqués, vous
devez spécifier le path complet avec des points comme séparateur.

<div class="field-message"
*ngIf="[Link]('[Link]').errors?.passwordStrength“
>
Your password must have lower case, upper case and numeric characters.
</div>

386
Reactive form
FormArray
 Dans les Reactive Form, un formulaire est défini à l'aide des API
FormControl et FormGroup, ou à l'aide de l'API FormBuilder dans la
plupart des cas, ou tous les champs d'un formulaire sont bien
connus à l'avance, permettant de définir un modèle statique.
 Imaginons d'autres situations plus avancées mais encore fréquemment
rencontrées où le formulaire est beaucoup plus dynamique et où
tous les champs du formulaire ne sont pas connus à l'avance
 Prenons le cas d’un formulaire dynamique dans lequel des contrôles
de formulaire sont ajoutés ou supprimés du formulaire par
l'utilisateur, en fonction de son interaction avec l'interface utilisateur.

387
Reactive form
FormArray
 Un FormArray, tout comme un FormGroup, est également un
conteneur de FormControl.
 Contrairement à un FormGroup, un conteneur FormArray ne nous
oblige pas à connaître tous les contrôles à l'avance, ainsi que
leurs noms.
 En fait, un FormArray peut avoir un nombre indéterminé de
FormControl, en commençant par zéro ! Les contrôles peuvent
ensuite être ajoutés et supprimés dynamiquement en fonction de la
façon dont l'utilisateur interagit avec l'interface utilisateur.

388
Reactive form
FormArray
 Chaque contrôle aura alors une position numérique dans le
tableau des contrôles de formulaire, au lieu d'un nom unique.
 Les contrôles de formulaire peuvent être ajoutés ou supprimés du
modèle de formulaire à tout moment lors de l'exécution à l'aide de
l'API FormArray.
 Pensez à spécifier un getter pour votre FormArray
this.fg2 = [Link]({ get skills() {
name: [null], return [Link]('skills') as FormArray;
age: [0], }
skills: new FormArray([]),
});
389
Reactive form
FormArray
 Afin de déclarer un FormArray dans un FormGroup, il suffit de
définir une propriété de type un objet FormArray et de
l’initialiser à un tableau vide ou contenant des FormControl ou
FormGroup selon votre cas d’utilisation.
[Link] = new FormGroup({
credentials: new FormGroup({
email: new FormControl(null, [[Link], [Link]]),
password: new FormControl(null, [[Link], createPasswordStrengthValidator]),
}),
username: new FormControl(null, [[Link]]),
age: new FormControl(null, [[Link], [Link]('[0-1]?\d{1,2}')]),
skills: new FormArray([]),
});
390
Reactive form
FormArray
 Afin de déclarer un FormArray avec le service FormBuilder, il suffit d’appeler
la méthode array de votre FormBuilder et de lui passer un tableau vide ou
contenant des group selon votre cas d’utilisation.

[Link] = [Link]({
credentials: [Link]({
email: [null, {validators: [[Link], [Link]]}],
password: [null, {validators: [[Link], createPasswordStrengthValidator()]}],
}),
username: [null, {validators: [[Link]]}],
age: [null, {validators: [[Link], [Link]('[0-1]?\d{1,2}')]}],
skills: [Link]([])
});

391
Reactive form
FormArray
 Dans la partie Template, vous devez identifier un container groupant tous ses
éléments.
 Ajouter y la directives formArrayName et associez la au champ FormArray.
 Boucler sur les éléments contenu dans votre FormArray. Si c’est des
FormGroup, binder la propriété formGroupName (formControlName si
vous avez uniquement des FormControl) avec l’indice du FormGroup dans le
<divarray.
formArrayName="skills">
Skills <button (click)="addSkill()" class="btn btn-success">Add Skill</button>
<div
*ngFor="let skillControl of [Link]; let i = index"
[formGroupName]=“i">
<input type="text" class="form-control" formControlName="name">
</div> 392
Reactive form
FormArray
Voici les méthodes les plus couramment utilisées disponibles dans l'API FormArray :
 controls : il s'agit d'un tableau contenant tous les FormControl qui font partie
du FormArray
 length : C'est la longueur totale du tableau
 at(index) : renvoie le FormControl à une position de tableau donnée
 push(control) : ajoute un nouveau FormControl à la fin du tableau
 insert(index, control, option) : ajoute un nouveau FormControl à la position
index
 removeAt(index) : supprime un FormControl à une position donnée du tableau
 getRawValue() : Obtient les valeurs de tous les contrôles de formulaire, via la
propriété [Link] de chaque FormControl.
 setValue(value, option, emitEvent): Définit la valeur de FormArray. Il accepte un
tableau qui correspond à la structure du contrôle.
393
Reactive form
FormArray
Ajout dynamique d’éléments
 Afin d’ajouter un élément dynamiquement, vous pouvez utiliser l’API
FormArray et sa méthode push.
 Préparer l’élément à ajouter et pusher le dans votre Array

addSkill(): void {
const skillFormGroup = [Link]({
name: [null, [Link]]
});
[Link](skillFormGroup);
}

394
Reactive form
Avantages
 Les Reactives Forms sont beaucoup plus propres, et se concentrent
uniquement sur la logique de présentation. De même pour la
logique de validation.
 Toutes les règles de validation métier ont été déplacées vers la classe
du composant. Ceci facilite les tests unitaires.
 La définition dynamique du formulaire devient plus facile avec les
FormArray.
 Il est beaucoup plus facile de créer un validateur personnalisé : il
suffit de définir une fonction et de se connecter à notre
configuration.
 Pour l’approche basée Template on doit passer par une directive.
395
Programmation Asynchrone

Programmation non bloquante.

396
Les promesses
 Ce sont des objets qui représentent une complétion ou l’échec d’une
opération asynchrone.
([Link]
les_promesses)
 Le fonctionnement des promesses est le suivant :
 On crée une promesse.
 La promesse va toujours retourner deux résultats :
 resolve en cas de succès
 reject en cas d’erreur
 Vous devrez donc gérer les deux cas afin de créer votre traitement
397
Promesse
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(3);
}, 5000);
});

[Link](
function (x) {
[Link]('resolved with value :', x);
}
)

398
Qu’est ce que la programmation réactive
1. Nouvelle manière d’appréhender les appels asynchrones
2. Programmation avec des flux de données asynchrones

Programmation réactive =
Flux de données (observable) + écouteurs d’événements(observer).

C’est un ensemble de réaction suite à des événements.

399
Qu’est ce que la programmation réactive
 La programmation réactive nous permet donc de réagir à divers
événements tel qu'une écoute sur un click, un timer ou un
interval qui déclenche un traitement, une requête http.
 Elle va nous permettre d’uniformiser la gestion de ses
événements.
 Actuellement pour une requête http c’est un fetch, pour un event
dans le navigateur c’est un addEventListener, …
 L’idée est donc de normaliser tout ca.
 Ceci nous permettra de les fusionner, les combiner …
400
Le pattern « Observer »
 Le patron de conception observer permet à un objet de garder la
trace d'autres objets, intéressés par l'état de ce dernier.

 Il définit une relation entre objets de type un-à-plusieurs.

 Lorsque l’état de cet objet change, il notifie ces observateurs.

401
Observables, Observers et subscriptions
Observable

inscription Données

observer observer observer

traitement
402
Fonctionnement

403
Promesse Vs Observable
Promesse Observable
Un promesse gère un seul événement Un observable gère un « flux » d’événements.
Non annulable. Annulable.
Lazy : le traitement n'est déclenché qu'à la première
Traitement immédiat.
utilisation du résultat.
Une centaine d'opérateurs de transformation natifs
Deux méthodes uniquement (then/catch).
(map, reduce, merge, filter, …).
Operateurs tels que retry, replay

404
Observable
const observable = new Observable(
(observer) => {
let i = 5;
setInterval(() => {
if (!i) {
[Link]();
}
[Link](i--);
}, 1000);
});
[Link](
(val) => {
[Link](val);
}
);

405
asyncPipe
 async Pipe est un pipe qui permet d’afficher directement un
observable.
 {{ valeurSourceAsynchrone | async }}
 L’asyncPipe s’inscrit automatiquement à l’observable et affiche le
dernier résultat envoyé.
 Quand le composant est détruit l’asyncPipe se désinscrit
automatiquement de l’observable.

406
Les operateurs de l’observable
Les opérateurs sont des fonctions. Il y a deux types d'opérateurs :
Les opérateurs de création, elles permettent de créer un Observable.
Par exemple : of(1, 2, 3) crée un observable qui va émettre 1, 2, et 3, l'un
après l'autre.
Un opérateur pipeable est une fonction qui prend un observable
comme entrée et renvoie un autre observable. C'est une opération pure :
le précédent Observable reste inchangé.
Syntaxe : [Link](opertaeur1(), operateur2(), …).

407
Les operateurs de création
Les opérateurs de création sont utilisés pour créer de nouveaux
observables.
Ils sont divisés en opérateurs de création et en opérateurs de
création de jointure.
La principale différence entre eux réside dans le fait que les opérateurs
de création de jointure créent des observables à partir d'autres
observables, alors que les opérateurs de création créent des
observables à partir d'objets qui diffèrent des observables.

408
Les operateurs de création
from
 from est utilisé pour convertir des types d'objets JavaScript
comme un tableau, une promesse ou un objet itérable en une
séquence observable de valeurs.
L'opérateur émet également une chaîne sous forme de séquence de
caractères.
from([1, 2, 3]).subscribe({
next: (data) => [Link]('[from]', data),
complete: () => [Link]('[from] complete'),
});

409
Les operateurs de création
of
 l’opérateur of est utilisé afin de convertir un argument en un
observable
 il ne fait aucun aplatissement ou conversion et émet chaque
argument sous le même type qu'il reçoit
 of est couramment utilisé lorsque vous avez simplement besoin de
renvoyer une valeur là où un observable est attendu ou de
démarrer une chaîne observable.
of([1, 2, 3], new Date(), {
name: 'sellaouti',
firstname: 'aymen',
}).subscribe({
next: (data) => [Link]('[of]', data),
})
410
Les operateurs de création
of

411
Les operateurs de création
timer
 L'opérateur timer est un opérateur de création utilisé pour créer un
observable qui commence à émettre les valeurs après un délai d'attente,
et la valeur continuera d'augmenter après chaque appel.
 Il prend en entrée en premier paramètre quand déclencher l’event.
 En second paramètre en cas de volonté de répétition chaque
combien de millisecondes reprendre l’émission.
timer(1000).subscribe((val) => [Link]('[timer] : '+ val));

timer(1000, 1000).subscribe((val) => [Link]('[timer] : '+ val));

412
Les operateurs de création
fromEvent
 L'opérateur fromEvent est utilisé pour émettre un observable en se
basant sur un événement.
 Il prend en paramètre l’élément cible puis l’événement à écouter.
export class FromEventComponent implements AfterViewInit {
@ViewChild('btn') button!: ElementRef;
onClickButton$!: Observable<Event>;
ngAfterViewInit() {
[Link]$ = fromEvent([Link], 'click');
}
}

413
Quelques opérateurs utiles de l’Observable
opérateur pipable
map

414
Exercice
 Ecrire un composant qui affiche une suite d’images non stop.
 Utiliser un observable comme source d’images.
 Vous devez uniquement utiliser des opérateurs pour faire le travail.
 La taille de l’image, la liste des images et le temps entre chaque image
doit être paramétrable.

415
Quelques opérateurs utiles de l’Observable
opérateur pipable
filter

416
Quelques opérateurs utiles de l’Observable
opérateur pipable
take
 L'opérateur take prend des valeurs de
la source observable à l'observateur
(mise en miroir) jusqu'à ce qu'il
atteigne le seuil de valeurs défini pour
l'opérateur.
Sur chaque valeur, take compare le
nombre de valeurs qu'il a mises en miroir
avec le seuil défini. Si le seuil est atteint,
il termine le flux en se désabonnant de
la source et en transmettant la
notification complète à l'observateur.
417
Quelques opérateurs utiles de l’Observable
opérateur pipable
throttleTime
 throttleTime prend en paramètres un nombre x
représentant le nombre de millisecondes.
 Au départ le timer est désactivé.
 Dés que la première valeur est émise, elle la laisse passer
et lance un timer pendant x ms.
 Pendant la durée du timer rien ne passe et même si une
nouvelle valeur arrive le timer reste inchangé.
 Une fois le timer fini, throttleTime refait la même chose
et attend la première émission pour reprendre le même
processus.
 Cas d’utilisation : Permettre à l’utilisateur de déclencher
une fois un évènement dans un intervalle de temps
donné.
Appeler une fonction une seule fois dans un intervalle
de temps particulier au survol de la souris.
418
Quelques opérateurs utiles de l’Observable
opérateur pipable
debounceTime
debounceTime retarde les valeurs
émises par une source pour le temps
d'échéance donné. Si dans ce délai une
nouvelle valeur arrive, la valeur en
attente précédente est supprimée et
l’intervalle est réinitialisé.
De cette façon, debounceTime garde
une trace de la valeur la plus récente et
émet cette valeur la plus récente lorsque
l'heure d'échéance donnée est dépassée.
L’autocomplete est le cas classique
du debounceTime
419
Les opérateurs d’aplatissement/ flattening
operators
 Une erreur courante commise dans les [Link](
applications Angular consiste à imbriquer les (params) => {
abonnements observables. [Link]([Link])
.subscribe(
 Cette syntaxe n'est pas recommandée car elle (personne) => {
est difficile à lire et peut entraîner des bogues [Link] = personne;
des effets secondaires inattendus. },
(erreur) => {
 Par exemple, cette syntaxe rend difficile la if (![Link]) {
désinscription correcte de tous ces observables. [Link](['cv']);
}
 De plus, si observable1 émet plus d'une fois }
dans un court laps de temps, nous pourrions );
vouloir annuler l'abonnement précédent à }
observable2 et en démarrer un nouveau basé sur );
les nouvelles données reçues d'observable1.
420
Les opérateurs d’aplatissement/ flattening
operators
 L'opérateur d’aplatissement sont des opérateur qui permettent
d’émettre un flux à partir d’un autre.
 Ils permettent d’éviter les inscriptions imbriquées avec subscribe.
 Il en existe plusieurs variantes et qui permettent de spécifier
comment gérer les flux récupérés :
 Est-ce qu’on est encore intéressé par l’inscription précédente ?
 Est-ce que l’ordre des inscriptions est important ?

421
Les opérateurs d’aplatissement/ flattening
operators
MergeMap
 L'opérateur mergeMap est essentiellement une combinaison de
deux opérateurs - merge et map.
 La partie map vous permet de mapper une valeur d'une source
observable à un flux observable. Ces flux sont souvent appelés flux
internes.
 La partie merge combine tous les flux internes observables renvoyés
par la map et émet simultanément toutes les valeurs de chaque flux
d'entrée.

422
Les opérateurs d’applatissement/ flattening
operators
MergeMap
 Au fur et à mesure que les valeurs de
toute séquence combinée sont produites,
ces valeurs sont émises dans le cadre de la
séquence résultante.
 Utilisez cet opérateur si vous n'êtes pas
concerné par l'ordre des émissions et
que vous êtes simplement intéressé par
toutes les valeurs provenant de
plusieurs flux combinés comme si elles
étaient produites par un seul flux.

[Link] 423
Les opérateurs d’applatissement/ flattening
operators
MergeMap
timerObs(timer: number, name: string, iteration = 4) { params = [
return new Observable((observer: Observer<string>) => { {name: 'obs1', timer: 1000, iteration: 4 },
let i = 0; {name: 'obs2', timer: 1500, iteration: 4 },
const x = setInterval(() => { ];
if (i >= iteration) { constructor(private rxjsService: RxjsService) {
[Link](); from([Link]).pipe(
clearInterval(x); mergeMap((param) => [Link]([Link],
} [Link]))
[Link](`observable ${name} ${++i}`); ).subscribe(
}, timer); (data) => [Link](data)
}); )
} }

424
Les opérateurs d’applatissement/ flattening
operators
SwitchMap
 L'opérateur switchMap est essentiellement une combinaison de deux
opérateurs - switchAll et map.
 La partie de map vous permet de mapper une valeur d'une source
observable d'ordre supérieur à un flux observable interne.
 La partie switch s'abonne à l'observable interne fourni le plus
récemment émis par un observable d'ordre supérieur, en se désabonnant
de tout observable interne précédemment souscrit.
L'opérateur switchMap effectue toutes les actions suivantes :
 Annule et se désabonne automatiquement du second observable lorsque le
premier émet une nouvelle valeur.
 Se désabonne automatiquement du second observable si nous nous
désinscrivons du premier.
 S'assure que les deux observables se produisent en séquence, l'un après l'autre.

425
Les opérateurs d’applatissement/ flattening
operators
SwitchMap
 switchMap n'a qu'un seul
abonnement actif à la fois à partir
duquel les valeurs sont transmises à un
observateur.
 Une fois que l'observable d'ordre
supérieur émet une nouvelle valeur,
switchMap exécute la fonction pour
obtenir un nouveau flux observable
interne et commute les flux. Il se
désabonne du flux actuel et s'abonne au
nouvel observable interne.

[Link] 426
Les opérateurs d’applatissement/ flattening
operators
SwitchMap
 Une erreur courante commise dans les [Link](
applications Angular consiste à imbriquer les (params) => {
abonnements observables. [Link]([Link])
.subscribe(
 Cette syntaxe n'est pas recommandée car elle (personne) => {
est difficile à lire et peut entraîner des bogues [Link] = personne;
des effets secondaires inattendus. },
(erreur) => {
 Par exemple, cette syntaxe rend difficile la if (![Link]) {
désinscription correcte de tous ces observables. [Link](['cv']);
}
 De plus, si observable1 émet plus d'une fois }
dans un court laps de temps, nous pourrions );
vouloir annuler l'abonnement précédent à }
observable2 et en démarrer un nouveau basé sur );
les nouvelles données reçues d'observable1.
427
Les opérateurs d’applatissement/ flattening
operators
SwitchMap
timerObs(timer: number, name: string, iteration = 4) { params = [
return new Observable((observer: Observer<string>) => { {name: 'obs1', timer: 1000, iteration: 4 },
let i = 0; {name: 'obs2', timer: 1500, iteration: 4 },
const x = setInterval(() => { ];
if (i >= iteration) { constructor(private rxjsService: RxjsService) {
[Link](); from([Link]).pipe(
clearInterval(x); switchMap((param) => [Link]([Link],
} [Link]))
[Link](`observable ${name} ${++i}`); ).subscribe(
}, timer); (data) => [Link](data)
}); )
} }

428
Les opérateurs d’applatissement/ flattening
operators
SwitchMap
export class SwitchMapComponent implements AfterViewInit {
search: string = '';
@ViewChild('searchInput') searchInput!: ElementRef;
constructor(private rxjsService: RxjsService) {}
ngAfterViewInit (): void {
const change$ = fromEvent<KeyboardEvent>([Link], 'keyup');
change$.pipe(
map((event: KeyboardEvent) => [Link]),
debounceTime(300),
switchMap((search) => {
return [Link](search)
})
)
.subscribe(
(input) => {//…Do what you want with your products}
)
}
}
429
Les opérateurs d’applatissement/ flattening
operators
ConcatMap
 L'opérateur concatMap est essentiellement une combinaison de deux
opérateurs - concat et map.
 La partie map vous permet de mapper une valeur d'une source observable à
un flux observable. Ces flux sont souvent appelés flux internes.
 La partie concat combine tous les flux internes observables renvoyés par
la map et émet séquentiellement toutes les valeurs de chaque flux
d'entrée.
Utilisez cet opérateur si l'ordre des émissions est important et que vous
souhaitez d'abord voir les valeurs émises par les flux qui passent par
l'opérateur en premier.

430
Les opérateurs d’applatissement/ flattening
operators
ConcatMap

[Link] 431
Les opérateurs d’applatissement/ flattening
operators
ConcatMap
timerObs(timer: number, name: string, iteration = 4) { params = [
return new Observable((observer: Observer<string>) => { {name: 'obs1', timer: 1000, iteration: 4 },
let i = 0; {name: 'obs2', timer: 1500, iteration: 4 },
const x = setInterval(() => { ];
if (i >= iteration) { constructor(private rxjsService: RxjsService) {
[Link](); from([Link]).pipe(
clearInterval(x); concatMap((param) => [Link]([Link],
} [Link]))
[Link](`observable ${name} ${++i}`); ).subscribe(
}, timer); (data) => [Link](data)
}); )
} }

432
Les opérateurs d’applatissement/ flattening
operators
ExhaustMap
 L'opérateur exhaustMap est essentiellement une combinaison de deux
opérateurs - exhaust et map.
 La partie map vous permet de mapper une valeur d'une source observable
d'ordre supérieur à un flux observable interne.
 La partie exhaust s'abonne ensuite à un observable interne et transmet des
valeurs à un observateur s'il n'y a pas déjà d'abonnement actif, sinon il ignore
simplement les nouveaux observables internes.
 exhaustMap n'a qu'un seul abonnement actif à la fois à partir duquel
les valeurs sont transmises à un observateur. Lorsque l'observable d'ordre
supérieur émet un nouveau flux observable interne, si le flux actuel n'est
pas terminé, ce nouvel observable interne est abandonné.
Une fois le flux actif en cours terminé, l'opérateur attend qu'un autre
observable interne s'abonne en ignorant les observables internes précédents.
433
Les opérateurs d’applatissement/ flattening
operators
ExhaustMap

[Link] 434
Les opérateurs d’applatissement/ flattening
operators
ExhaustMap
timerObs(timer: number, name: string, iteration = 4) { params = [
return new Observable((observer: Observer<string>) => { {name: 'obs1', timer: 1000, iteration: 4 },
let i = 0; {name: 'obs2', timer: 1500, iteration: 4 },
const x = setInterval(() => { ];
if (i >= iteration) { constructor(private rxjsService: RxjsService) {
[Link](); from([Link]).pipe(
clearInterval(x); exhaustMap((param) => [Link]([Link],
} [Link]))
[Link](`observable ${name} ${++i}`); ).subscribe(
}, timer); (data) => [Link](data)
}); )
} }

435
Les opérateurs d’applatissement/ flattening
operators
combineLatest
 combineLatest permet de fusionner plusieurs flux en prenant la valeur la plus
récente de chaque entrée observable et en émettant ces valeurs à l'observateur sous
forme de sortie combinée (généralement sous forme de tableau).
 Les opérateurs mettent en cache la dernière valeur pour chaque observable
d'entrée et seulement une fois que tous les observables d'entrée ont produit au
moins une valeur, il émet les valeurs mises en cache combinées à
l'observateur.
 Le flux résultant se termine lorsque tous les flux internes sont terminés et
génère une erreur si l'un des flux internes génère une erreur.
D'autre part, si un flux n'émet pas de valeur mais se termine, le flux résultant
se terminera au même moment sans rien émettre.
 De plus, si un flux d'entrée n'émet aucune valeur et ne se termine jamais,
combineLatest n'émettra jamais et ne se terminera jamais.

436
Les opérateurs d’applatissement/ flattening
operators
combineLatest
const cv$ = [Link](cvId)
.pipe(startWith(null));

const skills$ = [Link](cvId);

[Link]$ = combineLatest([cv$, skills$])


.pipe(
map(([cv, skills]) => {
return {
cv,
skills
}
})
);

437
Les opérateurs d’applatissement/ flattening
operators
combineLatest
const cv$ = [Link](cvId)
.pipe(startWith(null));

const skills$ = [Link](cvId)


.pipe(startWith([]));

[Link]$ = combineLatest([cv$, skills$])


.pipe(
map(([cv, skills]) => {
return {
cv,
skills
}
})
);

438
Les opérateurs d’applatissement/ flattening
operators
forkJoin
 forkJoin peut être appliqué à n'importe
quel nombre d'Observables. Lorsque tous
ces Observables sont terminés, forkJoin
émet la dernière valeur de chacun d'eux.
 Un cas d'utilisation courant pour forkJoin
est lorsque nous devons déclencher
plusieurs requêtes HTTP en parallèle avec
le HttpClient puis recevoir toutes les
réponses en même temps dans un tableau
de réponses.

439
Les opérateurs d’applatissement/ flattening
operators
forkJoin
forkJoin({
users: [Link]<User[]>('[Link]
posts: [Link]<Post[]>('[Link]
}).subscribe((usersAndPosts) => {
[Link] = [Link];
[Link] = [Link];
});

forkJoin([
[Link]<User[]>('[Link]
[Link]<Post[]>('[Link]
]).subscribe(([users, posts]) => {
[Link] = posts;
[Link] = users;
});

440
Exercice
 Sachant que pour sélectionner une personne dont le nom contient une chaine donnée, loopback
utilise la syntaxe suivante : {"where":{"name":{"like":"%${name}%"}}}
 Ceci doit être fourni dans les paramètres de votre requête avec la clé filter. Tester le sur votre
swagger.
 Ajouter un champ input dans la page Cv. A chaque caractère saisie, la liste des choix doit
automatiquement changer et n’afficher que les cvs qui contiennent la chaine saisie. En sélectionnant
un des choix, rediriger l’utilisateur vers les détails du cv sélectionné.
Faite en sorte d’avoir le code le plus récative possible.

441
Les opérateurs RxJs
Gestion des erreur
catchError
 L'opérateur catchError de RxJs permet de capturer les erreurs qui
se produisent dans un observable et de les traiter de manière
appropriée.
 Il permet de continuer à émettre des valeurs depuis un
observable même si une erreur se produit, plutôt que de stopper
complètement l'émission de valeurs.
 Il prend en argument une fonction qui prend en entrée l'erreur et
retourne un observable pour gérer cette erreur.
 Si vous voulez retourner l’erreur dans votre flux utilisez l’opérateur
throwError.
442
Les opérateurs RxJs
Gestion des erreur
catchError
[Link](
switchMap((param) => [Link](param['id'])),
catchError((e) => {
[Link](e);
[Link]([APP_ROUTES.cv]);
return throwError(() => new Error(e));
})
);

443
Les opérateurs RxJs
Gestion des erreur
catchError
 Vous pouvez utilisez l’opérateur EMPTY de rxjs pour spécifier que
vous n’émettez rien et qui émet ensuite une notification de
complétion du flux.
 Vous pouvez aussi émettre ce que vous voulez afin de continuer le
flux

catchError((e) => { catchError((e) => {


return EMPTY; return of([Link]());
}), }),

444
Exercice
 Reprenez les différentes fonctionnalités et faites en sorte que le code
devienne réactif.
 Utilisez l’async pipe à chaque fois que vous le pouvez.
 Utilisez catchError pour gérer les erreurs.

445
Quelques opérateurs utiles de l’Observable
[Link]

[Link]

[Link]

446
Les subjects
 Un subject est un type particulier d’observable. En effet Un subject
est en même temps un observable et un observer, il possède donc les
méthodes next, error et complete.
 Pour broadcaster une nouvelle valeur, il suffit d'appeler la méthode
next, et elle sera diffusé aux Observateurs enregistrés pour écouter le
Subject.

Observer
Observable
Next
(subscription) Error
Complete

447
Les subjects
personneClickSubject

Observable CvComponent

Details
Observer

Observver 3
448
Les behaviourSubject
 Les BehaviourSubject sont une variante du Subject.
 Le BehaviourSubject a la particularité de stocker la valeur "actuelle". Cela
signifie que vous pouvez toujours obtenir directement la dernière valeur émise
à partir du BehaviourSubject.
 Il existe deux façons d'obtenir cette dernière valeur émise. Vous pouvez soit
obtenir la valeur en accédant à la propriété value sur le BehaviourSubject,
 Soit vous y abonner. Si vous vous y abonnez, le BehaviourSubject émettra
directement la valeur actuelle à l'abonné. Même si l'abonné s'abonne beaucoup
plus tard que la valeur a été stockée.
 Vous pouvez créer un BehaviourSubject avec une valeur initiale qui sera donc
émise directement pout la première inscription.
449
Les ReplaySubject
 Le ReplaySubject est comparable au BehaviorSubject dans la mesure
où il peut envoyer les "anciennes" valeurs aux nouveaux abonnés.
 Il a cependant la particularité supplémentaire de pouvoir enregistrer
une partie de l'exécution de l’observable et donc de stocker
plusieurs anciennes valeurs et de les "rejouer" aux nouveaux
abonnés.
 Lors de la création du ReplaySubject, vous pouvez spécifier la
quantité de valeurs que vous souhaitez stocker et pendant
combien de temps vous souhaitez les stocker.

450
Les AsyncSubject
 Alors que BehaviorSubject et ReplaySubject stockent tous deux des
valeurs, AsyncSubject fonctionne un peu différemment.
L'AsyncSubject est une variante de l'objet où seule la dernière valeur
de l'exécution Observable est envoyée à ses abonnés, et uniquement
lorsque l'exécution est terminée.

451
Exercice
 Actuellement, dans notre application, nous utilisant une fonction
isAuthenticated qui retourne true ou false si le user est authentifié ou
non.
D’un autre coté nous ne sauvgardons aucune information sur le user
connecté.
 Nous voulons créer un store pour la gestion des utilisateurs nous
permettant de garder la trace du user authentifié ainsi que de gérer l’état
de l’utilisateur, à savoir qu’il est authentifié ou non.
 Choisissez la bonne structure

452
Exercice
 Faites en sortes d’avoir deux tabs dans la liste des cvs.
 Le premier tab affichera les cvs des juniors (age < 40)
 Le second tab affichera les cvs des seniors (age >= 40)

453
Opérateur de Multicasting
share
 L'opérateur share va multicatser les valeurs émises par une source
Observable pour les abonnés.
 Les multicatser signifie que les données sont envoyées à plusieurs
destinations.
 En tant que tel, le partage vous permet d'éviter plusieurs
exécutions de la source Observable lorsqu'il existe plusieurs
abonnements.
 share est particulièrement utile si vous avez besoin d'empêcher des
appels d'API répétés ou des opérations coûteuses exécutées par
Observables.
454
Opérateur de Multicasting
share
const sharedSource$ = interval(1000).pipe(
tap((ind) => [Link]('Processing: ', ind)),
map(() => [Link]([Link]() * 100)),
take(2),
share()
);
sharedSource$.subscribe((x) => [Link]('subscription 1: ', x));
sharedSource$.subscribe((x) => [Link]('subscription 2: ', x));
setTimeout(
() => sharedSource$.subscribe((x) => [Link]('subscription 3: ', x)),
1500
);
455
Opérateur de Multicasting
share
 Lorsque vous vous abonnez à un Observable partagé, vous vous abonnez
en fait à un Subject exposé par l'opérateur de partage.
 L'opérateur de partage gère également un abonnement interne à la
source Observable.
Le Subject interne est la raison pour laquelle plusieurs abonnés
reçoivent la même valeur partagée, car ils reçoivent des valeurs du sujet
exposé par l'opérateur de partage.
 share tient un compte des abonnés.
 Une fois que le nombre d'abonnés atteint 0, share se désabonnera de
l'Observable source et réinitialisera son Observable interne (le Subject).
 L'abonné (en retard) suivant déclenchera un nouvel abonnement à
l'Observable source, ou en d'autres termes, une nouvelle exécution de
l'Observable source.
456
Opérateur de Multicasting
share
const sharedSource$ = interval(1000).pipe(
tap((ind) => [Link]('Processing: ', ind)),
map(() => [Link]([Link]() * 100)),
take(2),
share()
);
sharedSource$.subscribe((x) => [Link]('subscription 1: ', x));
sharedSource$.subscribe((x) => [Link]('subscription 2: ', x));
setTimeout(
() => sharedSource$.subscribe((x) => [Link]('subscription 3: ', x)),
4500
);
457
Opérateur de Multicasting
shareReplay
 Dans certains cas, ce dont vous avez vraiment besoin, c'est d'un
partage capable de se comporter comme le ferait un ReplaySubject.
 Nous voulons donc qu’il partage à la fois la source Observable et
rejoue les dernières émissions pour les abonnés retardataires.
 Donc, il ne conserve pas le nombre d'abonnés par défaut, mais vous
pouvez utiliser l'option refCount avec une valeur true pour activer ce
comportement.
Contrairement à share, shareReplay expose un ReplaySubject aux
abonnés. ReplaySubject().

458
Opérateur de Multicasting
shareReplay
const sharedSource$ = interval(1000).pipe(
tap((ind) => [Link]('Processing: ', ind)),
map(() => [Link]([Link]() * 100)),
take(2),
shareReplay()
);
sharedSource$.subscribe((x) => [Link]('subscription 1: ', x));
sharedSource$.subscribe((x) => [Link]('subscription 2: ', x));
setTimeout(
() => sharedSource$.subscribe((x) => [Link]('subscription 3: ', x)),
4500
);
459
Opérateur de Multicasting
shareReplay
 Étant donné que shareReplay ne garde pas la trace d'un nombre
d'abonnés par défaut, il n'est pas en mesure de se désabonner de la
source Observable.
 Ceci est faisable si vous utilisez l'option refCount.
Afin d'utiliser shareReplay tout en vous débarrassant des problèmes
de fuite de mémoire, vous pouvez utiliser les options bufferSize et
refCount : shareReplay({ bufferSize : 1, refCount : true }).
 shareReplay ne réinitialise jamais son ReplaySubject interne
lorsque refCount atteint 0, mais se désabonne de la source Observable.
 Les abonnés en retard ne déclencheront pas une nouvelle exécution
de la source Observable et recevront jusqu'à N (bufferSize) émissions.
460
Opérateur de Multicasting
shareReplay
const sharedSource$ = interval(1000).pipe(
tap((ind) => [Link]('Processing: ', ind)),
map(() => [Link]([Link]() * 100)),
take(2),
shareReplay({ bufferSize: 2, refCount: true })
);
sharedSource$.subscribe((x) => [Link]('subscription 1: ', x));
sharedSource$.subscribe((x) => [Link]('subscription 2: ', x));
setTimeout(
() => sharedSource$.subscribe((x) => [Link]('subscription 3: ', x)),
4500
);
461
Hot Vs Cold Observable
 Les Cold Observables commencent à émettre des valeurs uniquement quand on s’y inscrit. Les
Hot observables, par contre émettent toujours.
 Les Cold Observables diffusent un flux par inscrit, ils sont unicast. Chaque nouvelle inscription
crée un nouveau contexte d’exécution.
 Les Hot observables, sont multicast, le même flux est partagé par tous les inscrits.
 Dans les Cold Observables, la source de données est à l’intérieur de l’observable.
 Dans les HotObservables, la source de données est à l’extérieur de l’observable.

462
Quelques exemples de Hot Observable
 fromEvent
 Les Subject RxJs
 Quelques opérateurs telque share() et shareReplay()

463
Exercice
 Modifier l’affichage des détails d’une personne au click. Enlever tous
les outputs et remplacer les par l’utilisation d’un subject.

464
Se désinscrire
 La méthode subscribe permet de s’inscrire à un observable.

 Problème : Cette souscription reste valide même après la disparition


de la variable ce qui sature la mémoire pour rien.

465
Se désinscrire
complete
 Lorsqu‘un observable se termine (méthode complete), tous les
abonnements sont automatiquement désabonnés.
 Si vous savez qu'un observable se terminera, vous n'avez pas à
vous soucier de nettoyer les abonnements.
 Cela est vrai pour tout observable créé à partir de plusieurs des
sources observables intégrées dans Angular telles que les méthodes
du HttpClient ou le service ActivatedRoute.
 Il n'y a pas de convention standard qui indique ce que les
observables peuvent terminer, ou quand, il appartient donc au
développeur de faire les recherches nécessaires.

466
Se désinscrire
async pipe
 La première et la plus courante solution pour gérer les
abonnements est l’async pipe.
 Plutôt que de vous abonner à des observables dans vos composants
et de définir des propriétés sur votre classe pour les données de
l'observable, préférez l’utilisation de l’async pipe.
 L’async pipe nettoie ses propres abonnements quand et selon les
besoins, vous déchargeant de cette responsabilité. Maintenant, cela
devrait être votre premier choix et fonctionnera pour la plupart
des scénarios.

467
Se désinscrire
async pipe
export class TestUnsbscribeComponent {
todos$: Observable<Todo[]>;
firstTodo$: Observable<Todo>;
constructor(private todoService: TodoService) {
[Link]$ = [Link]();
[Link]$ = [Link](1);
}
}

<div *ngIf="firstTodo$ | async as firstTodo">


The first todo is :
{{ [Link] }} | {{[Link]}}
</div>
<ol>
<li *ngFor="let todo of todos$ | async">
<pre>{{todo | json}}</pre>
</li>
</ol>
468
Se désinscrire
Et si on peut pas utiliser l’async pipe ?
 Que faire lorsque vous ne pouvez pas utiliser l’async pipe ? Il y a plusieurs
options.
 La première et la plus simple consiste à utiliser un Subscription pour collecter
tous les abonnements que vous créez, vous permettant de les nettoyer plus
tard au moment opportun.
 L'objet Subscription dans RxJs n'est pas seulement représentatif d'un seul
subscription à un flux. Il est également représentatif d'un agrégat de
subscription qui contient d'autres abonnements. Cela lui permet d'être utilisé
comme point de contrôle unique pour n'importe quel nombre d'abonnements.
 Pour se faire, utilisez sa méthode add qui prend en paramètre une
subscription.

469
Se désinscrire
Et si on peut pas utiliser l’async pipe ?
export class TestUnsbscribeComponent implements OnDestroy{
todos$: Observable<Todo[]>;
firstTodo$: Observable<Todo>;
subscription: Subscription;
nb = 0;
constructor(private todoService: TodoService, private testService: TestService ) {
[Link]$ = [Link]();
[Link]$ = [Link](1);
[Link] = [Link]$.subscribe(() => [Link] ++);
}

ngOnDestroy(): void {
[Link]();
}
}

470
Se désinscrire
Et si on peut pas utiliser l’async pipe ?
export class TestUnsbscribeComponent implements OnDestroy{
todos$: Observable<Todo[]>;
firstTodo$: Observable<Todo>;
subscription: Subscription = new Subscription();
nbClick = 0;
nbUpdates = 0;
constructor(private todoService: TodoService, private testService: TestService ) {
[Link]$ = [Link]();
[Link]$ = [Link](1);
[Link]([Link]$.subscribe(() => [Link] ++));
[Link]([Link]$.subscribe(() => [Link] ++));
}
ngOnDestroy(): void {
[Link]();
}

471
Se désinscrire
Utiliser l’opérateur takeUntilDestroyed
 L’opérateur takeUntilDestroyed de @angular/core/rxjs-interop
vous permet de gérer l’unsubscription.
 Cette fonctionnalité est disponible à partir d’angular 16

[Link]$.pipe(
map((element) => element * 3),
filter((element) => element % 2 == 0),
takeUntilDestroyed()
);

472
Se désinscrire
Utiliser un signal
 Une autre façon de se désabonner, et peut-être une manière plus
élégante, consiste à utiliser des signaux.
 Les signaux sont un moyen d'utiliser d'autres flux pour contrôler les
flux auxquels vous êtes peut-être abonné.
 Les signaux sont obtenus avec l'opérateur takeUntil dans le pipe et
tout autre observable. L'autre observable peut être un Subject ou un
autre observable ; tout ce qui émet un next.

473
Se désinscrire
Et si on peut pas utiliser l’async pipe ?
export class TestUnsbscribeComponent implements OnDestroy{
todos$: Observable<Todo[]>;
signal$ = new Subject();
nbClick = 0;
constructor(private todoService: TodoService, private testService: TestService ) {
[Link]$ = [Link]();
[Link]$
.pipe(takeUntil([Link]$))
.subscribe(() => [Link] ++);
}
ngOnDestroy(): void {
[Link]$.next('i am emiting stop emiting on your side :S');
[Link]$.complete();
}
}

474
4

signaux et rxJs
7
5

Interopérabilité
Les signaux ne sont pas la pour enlever RxJs mais plutôt pour limiter
son utilisation la ou il est le plus approprié de l’utiliser.
RxJS se marie mieux avec les tâches asynchrones alors que les signaux
sont synchrones.
Nous pouvons utiliser des signaux et des observables ensemble, et
nous pouvons les convertir l’un en l’autre.
Deux fonctions pour faire cela sont disponibles dans le tout
nouveau package : @angular/core/rxjs-interop
toObservable qui convertit un signal en un observable
toSignal qui convertit un observable en un signal. Elle prend en
premier paramètre l’observable et en second paramètre un objet
ToSignalOptions permettant de définir la valeur initiale.
4

signaux et rxJs
7
6

Interopérabilité

cvs = toSignal(
[Link](),
{ initialValue: [] }
);
Angular
HTTP
AYMEN SELLAOUTI

477
Objectifs
1. Mise en place du service HttpClient
2. Utiliser le service HttpClient
3. Comprendre le principe d’authentification via les tokens
4. Utiliser les protecteurs de routes (les guards)
5. Utiliser les Interceptors afin d’intercepter les requêtes Http
6. Déployer votre application en production

478
HTTP
 Angular est un Framework FrontEnd

 Pas d’accès à la BD

 Pas de possibilité de persistance des données

 Pas de puissance permettant des traitements lourds.

Le Service HttpClient

479
Installation de HTTP
 Afin d’utiliser le service HttpClient, vous devez le configurez en
utilisant la méthode provideHttpClient, que la plupart des
applications incluent dans la clé providers de votre [Link].
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(),
],
};

480
Installation de HTTP
 Une fois configuré, vous pouvez injecter votre services la ou vous
en avez besoin.

http = inject(HttpClient);

constructor(private http: HttpClient) {}

481
Configuration du HttpClient
 Par défaut, HttpClient utilise l'API XMLHttpRequest pour effectuer des
requêtes.
 La fonctionnalité withFetch permet au client d'utiliser l'API de récupération à
la place.
 fetch est une API plus moderne et est disponible dans quelques
environnements où XMLHttpRequest n'est pas pris en charge.
 Il présente quelques limitations, telles que le fait de ne pas produire
d'événements de progression du téléchargement.
providers: [
provideHttpClient(
withFetch()
)
], 482
Interagir avec une API Get Request
 Afin d’exécuter une requête get le module http nous offre une
méthode get.
 Cette méthode retourne un Observale.
 Cet observable a un objet contenant 3 callback function comme
paramètres.
 Une en cas de réponse next
 Une en cas d’erreur error
 La troisième en cas de fin du flux de réponse. complete

483
Interagir avec une API Get Request
[Link](API_URL).subscribe({
next:(response:Response)=>{
//ToDo with DATA
},
error: (err:Error)=>{
//ToDo with error
},
complete: () => {
[Link]('Data transmission complete');
}
});

484
Interagir avec une API POST Request
 Afin d’exécuter une requête POST le module http nous offre une
méthode post.
 Cette méthode retourne un Observale.
 Diffère de la méthode get avec un attribut supplémentaire : body
 Cette observable a 3 callback function comme paramètres.
 Une en cas de réponse
 Une en cas d’erreur
 La troisième en cas de fin du flux de réponse.

485
Interagir avec une API POST Request
[Link](API_URL,dataToSend).subscribe({
next:(response:Response)=>{
//ToDo with DATA
},
error: (err:Error)=>{
//ToDo with error
},
complete: () => {
[Link]('Data transmission complete');
}
});

486
Documentation

[Link]

487
Exercice
 Accéder au site [Link]
 Utiliser l’API des posts pour afficher la liste des posts. En attendant le
chargement des données afficher un message « loading… ».
 Ajouter un input. A chaque fois que vous écrivez un élément dans cet
input il sera ajouté dans la liste.

488
Les headers
 Afin d’ajouter des headers à vos requêtes, le HttpClient vous offre la classe
HttpHeaders.
 Cette classe est une classe immutable (read Only).
[Link]
 Elle propose une panoplie de méthode helpers permettant de la manipuler.
 set(clé,valeur) permet d’ajouter des headers. Elle écrase les anciennes valeurs.
 append(clé,valeur) concatène de nouveaux headers.
Toutes les méthodes de modification retourne un HttpHeaders permettant un
chainage d’appel.
[Link]

489
Les paramètres
 Afin d’ajouter des paramètres à vos requêtes, le HttpClient vous offre la classe
HttpParams.
 Cette classe est une classe immutable (read Only).
 Elle propose une panoplie de méthode helpers permettant de la manipuler.
 set(clé,valeur) permet d’ajouter des headers. Elle écrase les anciennes valeurs.
 append(clé,valeur) concatène de nouveaux headers.
Toutes les méthodes de modification retourne un HttpParams permettant un
chainage d’appel.
[Link]

490
Authentification

491
S’authentifier avec Loopback
Loopback offre un mécanisme d’’authentification prêt à l’emploi.
Avec l’api de la classe user il vous permet d’ajouter des users et de vous
connectez. Il permet aussi avec son module Loopback acl de créer des
restrictions sur l’api.
Une fois vos uri protégée, vous devez vous connecter pour pouvoir les
utiliser.
En vous connectant, il vous offrira un token. Vous devez l’utiliser à
chaque appel de votre api.

492
Installer loopback
 Nous allons installer la version lts :
 npm install -g loopback-cli
 Vérifier votre version avec lb –v
Créer votre première application avec la commande lb
Renseigner le nom de votre projet
Renseigner le type de votre projet
C’est fait

493
Loopback : création du modèle
 Créer votre modèle avec lb model

 Suivez les étapes, ajouter votre modèle et ajouter les champs qui le
compose.

Dans notre exemple nous allons utiliser un model « Personne »

494
Loopback : création du modèle

495
{
"name": "personne",
"base": "PersistedModel",
"idInjection": true,
"options": {

Modèle généré
"validateUpsert": true
},
"properties": {
"cin": {
"type": "number",
"required": true
},
"name": {
"type": "string",
"required": true
},
"firstname": {
 Dans ce cas nous allons utiliser "type": "string",
"required": true
une base de données MySql avec },
"age": {
un id en auto-increment. "type": "number",
"required": true
},
"path": {
"type": "string",
"required": true
},
"job": {
"type": "string",
"required": true
}
},
496
Associer votre modèle à une base de
données MySql
 Installer le mysql-connector
npm install loopback-connector-mysql --save
 Configurer votre datasource dans le fichier [Link] et ajouter
la configuration de votre base de données mySql.
 Associer la datasource à votre modèle dans le fichier model-
[Link].

497
Associer votre modèle à une base de
données MySql
{
"db": {
"name": "db",
"connector": "memory" .
}, .
"personneDb": { .
"host": "localhost",
"port": 3306, "Note": {
"url": "", "dataSource": "db"
"database": "test_pdo", },
"password": "", "personne": {
"name": "personneDb", "dataSource": "personneDb",
"user": "root", "public": true
"connector": "mysql" }
} [Link]
}
[Link]

498
Lancer votre application
 Pour lancer votre application exécuter la commande node .

 Accéder à votre swagger en utilisant l’adresse [Link]

 Tester votre api.

499
Exercice
 Associer votre application à votre API Loopback. Tester les fonctionnalités d’ajout de suppression
et de modification et de sélection de l’ensemble des personnes.

 Sachant que pour sélectionner une personne dont le nom contient une chaine donnée, loopback
utilise la syntaxe suivante :
{"where":{"name":{"like":"%${name}%"}}}
 Ceci doit être fourni dans les paramètres de votre requête avec la clé filter. Tester le sur votre
swagger.
 Ajouter un champ input. A chaque caractère saisie, la liste des choix doit automatiquement changer
et n’afficher que les cvs qui contiennent la chaine saisie. En sélectionnant un des choix, rediriger
l’utilisateur vers les détails du cv sélectionné.

500
Ajouter une acl avec loopback
 Nous voulons ajouter des contraintes d’accès à notre application.
 Nous voulons que seul les utilisateurs connectés puissent ajouter et supprimer des personnes.
 Ajouter les ACL en utilisant la commande lb acl.

501
Ajouter une acl avec loopback
Vérifier que votre modèle a été modifié :

"acls": [
{
"accessType": "WRITE",
"principalType": "ROLE",
"principalId": "$unauthenticated",
"permission": "DENY"
}

502
Ajouter une acl avec loopback
 Essayer d’ajouter une personne avec une requête de type POST. Utiliser directement votre swagger.

 Que remarquer vous ?

503
Ajouter le token dans la requête
 Si la ressource demandé est contrôlé avec un token, vous devez y insérer le token afin d’être
authentifié au niveau du serveur.
 Pour ajouter un token vous pouvez le faire via un objet HttpParams. Cet objet possède une
méthode set à laquelle on passe le nom du token ‘access_token’ suivi du token.
 Vous devez ensuite l’ajouter comme paramètre à votre requête.
const params = new HttpParams()
.set('access_token', [Link]('token'));
return [Link]([Link], personne, {params});

[Link]

504
Ajouter le token dans la requête
 Une seconde méthode consiste à ajouter dans le header de la requête avec comme name
‘Authorization’ et comme valeur ‘bearer’ à laquelle on concatène le Token.
 Pour se faire, créer un objet de type HttpHeaders.
 Utiliser sa méthode append afin d’y ajouter ses paramètres.
 Ajouter la à la requête.

const headers = new HttpHeaders();


[Link]('Authorization', 'Bearer ${token}');
return [Link]([Link], personne, {headers});

505
Sécuriser vos routes

 Dans vos applications, certaines routes ne doivent être accessibles que si vous êtes authentifié. Ce
cas d’utilisation se répète souvent et s’est un sous cas de la sécurisation de vos routes.

 Angular a pris en considération ce cas en fournissant un mécanisme via l’utilisation des Guard.

506
Guard
 Ce sont des classes (jusqu’à Angular 14) ou des fonctions (à partir d’Angular 14)
qui permettent de gérer l’accès à vos routes.
Un guard informe sur la validité ou non de la continuation du process de
navigation en retournant un booléen, une promesse d’un booléen ou un observable
d’un booléen.
 Le routeur supporte plusieurs types de guards, par exemple :
CanActivate permettre ou non l’accès à une route.
CanActivateChild permettre ou non l’accès aux routes filles.
CanDeactivate permettre ou non la sortie de la route.

507
Guard
A partir d’Angular 14 et la possibilité d’utiliser la fonction inject dans tous les
contexte d’injection, Angular préconise les fonctionnals Guards.

export const myGuard: CanActivateFn = (route, state) => {


return true;
};

508
Guard / canActivate 3

{
path: 'lampe',
component: ColorComponent,
canActivate: [AuthGuard]
},

509
Exercice
 Ajouter les guards nécessaires afin de sécuriser vos routes.

 Une personne déjà connectée ne peut pas accéder au composant de


login.

510
Guard / canActivateChild
 Le guard canActivateChild est un service Angular qui permet de
protéger les routes enfants en vérifiant si l'utilisateur a les autorisations
appropriées pour accéder à ces routes.
 Il retourne true si l'utilisateur a les autorisations requises et false sinon.
Cela permet de contrôler l'accès aux pages enfants d'une route donnée.

511
Exercice
 Toutes les routes commençant par /cv ne doivent être accessible que
pour les personnes authentifiées.

512
Guard / canDeactivate
 Le guard canDesactivate est un service Angular qui permet de
bloquer la sortie d’une route.
 Il retourne true si l'utilisateur a les autorisations requises pour quitter
la route et false sinon.
 Il est généralement utilisé pour s’assurer de ne pas quitter une page au
cas ou il y a des données non encore enregistrées ou s’il y a un upload
en cours et que vous ne voulez pas quitter la page tant que ce n’est pas
encore terminé.
Il ne s’applique pas aux ‘children Routes’.

513
Guard / canDeactivate
 Il est fortement couplé au composant
@Component({
selector: 'app-form', canDeactivate(
template: ` component: FormComponent,
<form> currentRoute: ActivatedRouteSnapshot,
<input [(ngModel)]="formData" name="formData"> currentState: RouterStateSnapshot,
</form> nextState?: RouterStateSnapshot
<button (click)="save()">Save</button> ): Observable<boolean> | Promise<boolean> | boolean {
` if (![Link] && [Link] !== '') {
}) return confirm('Are you sure you want to leave? You have
export class FormComponent implements OnInit, unsaved changes.');
CanDeactivate<FormComponent> { }
formData: string; return true;
saved = false; }
save() { }
[Link] = true;
} 514
Exercice
 Faite en sorte que lorsque vous êtes dans le TodoComponent, si un
des champs est rempli et que l’utilisateur veux quitter la page, afficher lui
un message de confirmation pour lui demander s’il est sur de vouloir
quitter la page.

515
Les intercepteurs
 A chaque fois que nous avons une requête à laquelle nous devons ajouter le token, nous devons
refaire toujours le même travail.
 Pourquoi ne pas intercepter les requêtes HTTP et leur associer le token s’il est la à chaque fois ?
 Un intercepteur Angular (fournit par le client HTTP) va nous permettre d’intercepter une
requête à l’entrée et à la sortie de l’application.
 Un intercepteur est soit une fonction ( angular 17 ) ou une classe qui implémente l’interface
HttpInterceptor.
 En implémentant cette interface, chaque intercepteur va devoir implémenter la méthode intercept.

Front intercepteur back

516
Les intercepteurs
import { HttpInterceptorFn } from "@angular/common/http";
export const newInterceptor: HttpInterceptorFn = (req, next) => {
return next(req);
};
import { Injectable } from '@angular/core';
import {HttpRequest,HttpHandler,HttpEvent,HttpInterceptor,} from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
intercept( request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
return [Link](request);
}
}

517
Les intercepteurs
 Un intercepteur est injecté au niveau du provider.
 Si vous utilisez un intercepteur fonctionnel, vous utilisez la fonction
withInterceptors qui est une des option de provideHttpClient.
Elle prend en paramètre un tableau d’intercepteur

bootstrapApplication(AppComponent, {providers: [
provideHttpClient(
withInterceptors([firsInterceptor, secondInterceptor]),
)
]});

518
Les intercepteurs
 Un intercepteur est injecté au niveau du provider.
 Si vous utilisez une classe vous devez passez par la définition du
provider puis appeler la méthode
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(
// DI-based interceptors must be explicitly enabled.
withInterceptorsFromDi(),
),
{provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptor, multi: true},
]}

519
Les intercepteurs : changer la requête
 Les instances HttpRequest et HttpResponse sont immuables. Ceci
implique que les intercepteurs ne peuvent pas les modifier
directement.
 Solution : les cloner, à l'aide de la méthode clone() et en spécifiant
quelles propriétés doivent être mutées dans la nouvelle instance.

520
Cloner une requête
const newReq = [Link]({
headers: new HttpHeaders()
// faites ce que vous voulez ici ajouter des headers, des params …
});
// Chainer la nouvelle requete avec [Link]
return [Link](newReq);

521
Intercepter les réponses
 Afin d’intercepter les réponses ou les erreurs, il faut récupérer la
réponse et vérifier s’il y a une erreur. Dans ce cas, il faut faire le
traitement souhaité.
return next(req).pipe(
tap((response) => {
if ([Link] === [Link]) {
[Link]( [Link], "returned a response with status", [Link] );
}
}),
catchError((e) => {
[Link]("Error !!!"); [Link]({ e });
return EMPTY;
})
);
522
Angular
Les variables d’environnement
 Avec Angular, vous pouvez définir plusieurs configurations propre à chaque
environnement que vous définissez.
 Avant Angular 15, ces variables été prêtes à la création du projet.
 Avant la version 15.1 d’Angular il faut les créer manuellement
 A partir de la version 15.1, vous pouvez les générer à partir du CLI avec la
commande ng generate environments
 Cette commande va créer un dossier environments sous le dossier src avec deux
fichiers, [Link] et [Link]

523
Angular
Les variables d’environnement
 Maintenant, dans le fichier [Link], et dans chaque bloc de l’environnement
cible, vous allez dire à Angular quel fichier doit remplacer le fichier de base.
 Dans cet exemple, on spécifie à Angular d’utiliser le fichier
[Link] lorsqu’on fait appel à [Link], lorsqu’on lance le
serveur en devellopement.
"development": { export const environments = {
… test: 'prod',
"fileReplacements": [ }
{ [Link]
"replace": "src/environments/[Link]",
"with": "src/environments/[Link]"
} export const environments = {
] test: 'prod',
} }
[Link]
},
}, 524
Angular
Les variables d’environnement
 Vous devez toujours appelez le fichier de base dans [Link]
 C’est en utilisant les remplacement qu’Angular saura quel fichier appeler.
import { environments } from '../environments/environments';
@Component({
selector: 'app-root',
templateUrl: './[Link]', export const environments = {
styleUrls: ['./[Link]'], test: 'prod',
}) }
[Link]
export class AppComponent {
constructor() {
[Link]('env', [Link]);
export const environments = {
}
test: 'prod',
}
}
[Link]

525
Angular
Les variables d’environnement
 Pour spécifiez quel environnement utiliser, ajouter l’option --configuration que
ce soit au build ou au serve.
 ng build --configuration=staging
import { environments } from '../environments/environments';
@Component({
selector: 'app-root', export const environments = {
templateUrl: './[Link]', test: 'prod',
styleUrls: ['./[Link]'], }
[Link]
})
export class AppComponent {
constructor() { export const environments = {
[Link]('env', [Link]); test: 'prod',
} }
} [Link]

526
Exercice
 Créer deux fichiers de configuration prod et dev et faite en sorte de
les utiliser et de les tester.

527
Angular
Les stratégies de compilation
Il existe deux techniques de compilation dans Angular:
Just-In-Time (JIT) Compilation: La compilation JIT se produit à
l'exécution du code. Le compilateur Angular est intégré au navigateur et
compile le code à chaque fois que l'application est lancée. Cela signifie que le
code est compilé en temps réel.
Ahead-of-Time (AOT) Compilation: La compilation AOT se produit
à la génération du code, avant que l'application ne soit exécutée. Le
compilateur Angular compile le code en un fichier JavaScript statique qui est
exécuté directement par le navigateur sans nécessiter de compilation
supplémentaire.

528
Angular
Les stratégies de compilation

[Link] 529
Angular
Les stratégies de compilation
Pourquoi choisir le AOT
Rendu plus rapide Avec AOT: le navigateur télécharge une version pré-compilée
de l'application. Le navigateur charge le code exécutable afin qu'il puisse restituer
l'application immédiatement, sans attendre de compiler l'application au préalable.
 Moins de requêtes asynchrones Le compilateur intègre des modèles HTML
externes et des feuilles de style CSS dans le JavaScript de l'application, éliminant
ainsi les requêtes ajax distinctes pour ces fichiers source.
 Taille de téléchargement du framework Angular plus petite Il n'est pas
nécessaire de télécharger le compilateur Angular si l'application est déjà compilée.
Le compilateur est à peu près la moitié d'Angular lui-même, donc l'omettre réduit
considérablement la charge utile de l'application.
Cependant ceci n’est pas toujours valide pour les grandes applications puisque
on charge le code compilé qui est plus lourd. PENSEZ AU LAZY LOADING
[Link] 530
Angular
Les stratégies de compilation
Pourquoi choisir le AOT
 Détecter les erreurs de modèle plus tôt Le compilateur AOT détecte
et signale les erreurs de liaison de modèle pendant l'étape de
construction avant que les utilisateurs ne puissent les voir.
 Meilleure sécurité AOT compile les modèles et composants HTML
dans des fichiers JavaScript bien avant qu'ils ne soient servis au client.
Sans modèles à lire et sans évaluation HTML ou JavaScript risquée
côté client, il y a moins de possibilités d'attaques par injection.

[Link] 531
Déploiement
 Afin de déployé votre application, il vous suffit d’utiliser la commande
suivante :
ng build --prod
 Un dossier dist sera créer contenant votre projet
 Pour tester localement votre projet, télécharger un serveur HTTP
virtuel avec la commande suivante :
Npm install http-server –g
 Lancer maintenant votre projet à l’aide de cette commande :
http-server dist/NomDeVotreProjet
[Link] 532
Déploiement
 Afin de voir le comportement de votre build dans des conditions qui
s’approche des conditions réelles de déploiement, lancez la commande
ng build --watch
 Ensuite ouvrez un second terminal et lancer votre serveur local.
 Si vous utilisez http-server lancez la commande
http-server dist/NomDeVotreProjet
 Dans votre serveur d’application, faite en sorte d’avoir une redirection de
toutes les routes de votre application différentes de la page d’accueil.
 Ceci est différent d’un serveur à un autre
 Testons le avec Github

[Link] 533
Déployer votre application sur GitHub
Pages (angularCli >= 8.3)
 Créer un repository et mettez y votre projet
 installer angular-cli-ghpages : ng add angular-cli-ghpages@next
Ajouter cette configuration dans votre fichier [Link] si ca n’a pas été fait automatiquement
"deploy": {
"builder": "angular-cli-ghpages:deploy",
}
Lancer la commande ng deploy --base-href=/the-repositoryname/
Accéder à votre page [Link]
 En cas de mise à jour, relancer le même code.

[Link]

534
Déployer votre application sur GitHub
Pages (angularCli >= 8.3)
Ajouter cette configuration dans votre fichier [Link] et utiliser uniquement ng deploy

"deploy": {
"builder": "angular-cli-ghpages:deploy",
"options": {
"baseHref": "[Link]
}
}

535
Mesurer les performances
 Afin d’optimiser le chargement de votre application, vous devez
faire en sorte d’avoir le bundle le plus optimisé possible.
 Afin d’analyser votre bundle, vous pouvez utiliser l’outil
source-map-explorer :
[Link]
 Afin d’installer cet outil lancez la commande :
npm install source-map-explorer --save-dev
 Lancez le build en incluant source-map : ng build --source-map
 Vous allez remarquez l’existence de fichiers ‘.map’

[Link] 536
Mesurer les performances
 Maintenant pour visualiser la sourceMap de votre code lancer la
commande :
source-map-explorer dist/**/*.js
 Préférez ajouter la commande dans votre fichier [Link]

"explorer":"source-map-explorer dist/**/*.js",
"explorer:main":"source-map-explorer dist/**/main*.js"

[Link] 537
Mesurer les performances

538
Mesurer les performances
 Vous pouvez utiliser le plugin lighthouse
 Il vous permet de générer un rapport sur votre application sur
plusieurs catégories
[Link]
nefehnmjammfjpmpbjk?hl=fr

539
Mesurer les performances
 Maintenant pour visualiser la sourceMap de votre code lancer la
commande :
source-map-explorer dist/**/*.js
 Préférez l’ajout de la commande dans votre fichier [Link]
"explorer":"source-map-explorer dist/**/*.js",
"explorer:main":"source-map-explorer dist/**/main*.js"

[Link] 540
Mesurer les performances
 Dans [Link], vous pouvez configurer source-map avec la
propriété sourceMap.
 Elle prend comme paramètre un booléen ou un objet avec les 4
options possibles "development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": {
"hidden": false,
"scripts": true,
"styles": true,
"vendor": false
},
[Link] 541
Mesurer les performances
Quelques techniques pour améliorer votre
Bundle Size
 En analysant le contenu de votre bundle, vous allez remarquer que
certaines bibliothèques prennent beaucoup d’espace.
 Si l’une de vos bibliothèque prend beaucoup d’espace, que’elle est
implémenté en CommonJS et que vous l’utilisez pour juste une ou
deux fonctionnalités, essayer de les implémentez.
 Si elle présente une grande complexité, essayez de chercher une
autre bibliothèque moins lourde.
 Préférez les Bibliothèques ES6 et +.

[Link] 542
Mesurer les performances
Quelques techniques pour améliorer votre
Bundle Size
 Vous devez changer votre code afin qu’il s’aligne et qu’il soit optimal
pour le tree-shaking.
 Le tree-shaking est le mécanisme qui permet d’éliminer le code
mort; C’est-à-dire le code qui ne sert à rien.
 Webpack lors de l’opération du bundling va détecter le dead code et
le marquer comme ‘module non utilisé’.
 Ensuite des outils comme UglifyJS, vont uglifyer votre code et en
même temps éliminer le dead code.
 L’idée est donc d’aider Webpack à trouver ce code inutile.

[Link] 543
Mesurer les performances
Quelques techniques pour améliorer votre
Bundle Size

[Link] 544
Mesurer les performances
Quelques techniques pour améliorer votre
Bundle Size
 N’importez jamais toutes les fonctions de votre bibliothèques.
 Ceci impliquera que vous avez besoins de toutes la bibliothèque. Ceci
implique que leur code, même si vous ne l’utilisez jamais, sera inclut
dans votre bundle et ne sera pas comptabilisé comme dead code.

 Dans le cas ici de moment, la solution est d’implémentez votre


fonction ou d’utilisez une bibliothèque ES6+ telque date-fns et qui soit
compatible avec le tree-shaking
[Link] 545
Mesurer les performances
Quelques techniques pour améliorer votre
Bundle Size
 Pour loadash, préférez ‘lodash-es’ qui exporte chaque fonction à
part.

[Link] 546
Mesurer les performances
Quelques techniques pour améliorer votre
Bundle Size
 Pour la partie css. Si vous importez des fichiers css au niveau de vos
composants plusieurs fois, ceci impliquera la duplication de ses
styles.
 N’importez que ce qui est spécifique dans votre composant.

[Link] 547
Angular
Les modules
AYMEN SELLAOUTI

548
Qu’est ce qu’un module
 C’est une classe avec une décoration NgModule
 Un module est un conteneur qui englobe un ensemble de fonctionnalités
liées.
 Les applications Angular sont modulaire.
 Un application Angular contient au moins un Module : AppModule.
 Un Module Angular peut contenir des composants, des provider de service…
 Une application simple est généralement composé d’un seul module. Par contre
dès que votre application grandit penser à la séparer en Modules.
 Chaque module vie séparée des autres modules. Par défaut il n’expose rien,
tous ce qui est à l’intérieur du module reste uniquement dans le module tant
qu’on ne l’exporte pas.
 Lorsqu’on importe un module, on importe réellement tous ce qu’il exporte.
[Link] 549
Rôle d’un module
 Organise votre application en des briques fonctionnelles

 Etend votre application avec des librairies externes

 Permet d’agréger et d’exporter des briques fonctionnels

 Faciliter le développement et la maintenance de votre application

550
Définition d’un module
L’annotation (decorator) @NgModule identifie
un module Angular. import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
L’annotation prend en paramètre un objet spécifiant à
import { AppComponent } from './[Link]';
Angular comment compiler et lancer l’application.
imports : tableau contenant les modules utilisés. @NgModule({
imports: [ BrowserModule ],
declarations : tableau contenant les classes de vue appartenant
declarations: [ AppComponent ],
à ce module, i.e. composants, directives et pipes de l’application. bootstrap: [ AppComponent ]
exports : tableau des classes de vues à exporter. })
providers : déclaration des services export class AppModule { }
bootstrap : indique le composant exécuter au lancement de
l’application et elle ne concerne que le module racine.

551
declarations
 Dans la partie declarations, faite en sorte que chaque composant,
directive ou pipe soit associé à un et un seul module. Ne déclarer pas un
même composant dans deux modules différents.
 Ne déclarer que les composants, directives et les pipes dans cette partie.
 Tous les composants, directives et les pipes déclarés sont privés par
défaut. Ils ne sont accessible que pour les composants, directives et les pipes
déclarés dans le même module.
 Pour utiliser un de ces éléments à l'extérieur du module, il faudra penser à
les exporter.

552
Routing
 Vous pouvez créer les routes de vos modules de la même manière
que pour le routing de l’AppModule.

Cependant, au lieu d’utiliser [Link] vous devez


utiliser la méthode forChild du RouterModule.

553
Exemple pour le TodoModule
import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core";
import { NgModule } from "@angular/core"; import { Route, RouterModule } from "@angular/router";
import { FormsModule } from "@angular/forms"; import { NF404Component } from "../nf404/[Link]
nent";
import { TodoComponent } from "./[Link]"; import { TodoComponent } from "./[Link]";
import { TodoRouting } from "./[Link]";
const routes: Route[] = [
@NgModule({ { path: "todo", component: TodoComponent },
declarations: [TodoComponent], { path: "**", component: NF404Component },
imports: [CommonModule, FormsModule, TodoRouting], ];
// Si vous n’avez pas besoin de TodoComponent à l’exterieur,
ne l’exporter pas @NgModule({
exports: [TodoComponent], imports: [[Link](routes)],
}) exports: [RouterModule],
export class TodoModule {} })
export class TodoRouting {}

554
Exercice
 Implémentez le CvModule.

555
Shared Module
FirstModule SecondModule

SharedModule
FirstComponent FirstComponent

SecondComponent SecondComponent

GenericDirective GenericDirective

SpecificFeature1Component SpecificFeature2Component

556
Shared Module
@NgModule({
declarations: [
FirstComponent,
SecondComponent,
GenericDirective,
],
imports: [
CommonModule
]
exports: [
FirstComponent,
SecondComponent,
GenericDirective,
]
})
export class SharedModule {
}
557
Lazy Loading
 Par défaut, tout les modules que vous
déclarer au niveau du AppModule sont
chargé au lancement de l’application.
 Ceci pose un problème au niveau du
Bundle généré de votre application.
 Une grande application aura une taille assez
conséquente ce qui peut provoquer un
problème au chargement de l’application et
donc un problème d’expérience utilisateur.
 L’idée du lazyLoading et de charge au
départ le module principale et puis de ne
charger un module que si on appelle l’une de
ses routes.
 Ceci va nous faire gagner en performance.

558
Lazy Loading
 Afin d’implémenter le Lazy Loading, on doit suivre les étapes suivantes :
1. Le module à charger doit lui-même gérer sa partie routing
2. Au niveau du routing principal (AppRoutingModule), créer une route, ajouter lui un path ‘ca sera
le préfixe de toutes les routes du module’, et ajouter une nouvelle clé qui est loadChildren.
Cette clé va informer angular et lui demander de ne charger le module associé que lorsque on
appelle le path défini.
3. Ce paramètre prend ou une chaine de caractère qui spécifie le module à charger ou une
callback function.
4. Finalement enlever les imports des modules lazy loaded au niveau du AppModule
{
{ path: "cv",
path: "cv", loadChildren: () => import('./cv/[Link]').then(
loadChildren: "./cv/[Link]#CvModule", m => [Link]
}, ),
}, 559
Exercice
 Testez le lazy loading avec le CvModule

560
Lazy Loading
Injecteur fils
 Lorsque vous utilisez le lazy loading, un Injecteur est
automatiquement crée lors du chargement de votre module. C’est le
‘child injector’ ou injecteur fils.
 Lorsque vous importer un module X, généralement tous les modules
qui l’importent se partage les mêmes instances providées par ce
module.
 Cependant, lorsque un module Y lazy loaded importe le module X, il
n’utilise pas le module Injector maos son propre injector
Ceci implique la création d’une nouvelle instance des services qu’il
provide.

561
Lazy Loading
Injecteur fils
Le pattern forRoot/ forChild
 Dans certains cas, le fait d’avoir plusieurs instances ne dérange pas,
mais ceci n’est pas toujours le cas. Prenons l’exemple du RouterModule
ou on veut avoir une instance commune de notre RouterService pour
toute l’application.
 Afin de gérer ca on ne va plus importer le Module entier mais une
partie du module.
 La partie import peut accepter un module ou une classe de type
ModuleWithProvider.
 Ceci vous permet de spécifier ce que vous voulez provider exactement

562
Lazy Loading
Injecteur fils
Le pattern forRoot/ forChild

static forRoot(): ModuleWithProviders<TestModule> {


return {
ngModule: TestModule,
providers: [TestforRootforChildService]
}
}

563
Lazy Loading
Injecteur fils
Le pattern forRoot/ forChild
 Un deuxième avantage du forRoot / forChild Pattern est de pouvoir
avoir des modules configurables.
 Votre méthode forChild peut avoir des paramètres que vous
injecter pour les récupérer et paramétrer vos services.

564
Guard / canLoad
 Le guard canLoad est un service Angular qui permet de conditionner
le chargement d’un module.
 Il est généralement associé à un module chargé en utilisant le
lazyLoading.
 Ceci permet d’optimiser le chargement du module. Si vous n’avez pas
le droit d’accéder au module, pourquoi le charger ?
@Injectable({
providedIn: 'root'
})
export class CanLoadAuthGuard implements CanLoad {
constructor(private router: Router) {}
canLoad(route: Route, segments: UrlSegment[]): Observable<boolean> {
return true;
}
} 565
Guard / canLoad
 Le Guard canLoad peut être depricated à cause de sa non gestion des
lazy loaded Component.
 Une alternative qui peut remplacer le canLoad est le canMatch
Guard proposé dans la version 14 d’Angular.

566
Exercice
 Votre CvModule étant lazy loaded, faite en sorte qu’il ne soit chargé
que si la personne est authentifié.

567
Guard / canMatch

[Link] 568
Guard / canMatch
 Le Guard canMatch permet de spécifier si le router doit matcher
(et non activer) une route ou non.
 Ceci diffère des autres guard. En effet, dans ce cas le routeur ira voir
les autres routes et continuera le processus de matching.

569
Guard / canMatch
@NgModule({
imports: [
[Link]([
{
path: 'team/:id',
component: TeamComponent,
loadChildren: () => import('./team').then(mod => [Link]),
canMatch: [CanMatchTeamSection]
},
{
path: '**',
component: NotFoundComponent
}
])
],
providers: [CanMatchTeamSection, UserToken, Permissions]
})
class AppModule {} 570
Guard / canMatch
{
path: 'team/:id',
component: TeamComponent,
loadChildren: () => import('./team').then(mod => [Link]),
canMatch: [(route: Route, segments: UrlSegment[]) => true]
},

{
path: 'team/:id',
component: TeamComponent,
loadChildren: () => import('./team').then(mod => [Link]),
canMatch: [(route: Route, segments: UrlSegment[]) => {
const authService = inject(AuthService);
return [Link]();
}]
},
571
Exercice
 Créer deux routes home, une pour les personnes authentifiés et l’autre
pour les visiteurs.
 Faite en sorte d’avoir la même route mais d’afficher le bon composant
en se basant sur le fait qu’il soit authentifié ou non.
 Imaginez le même scénario pour un DashboardComponent

572
Preloading Lazy Loading
 Le problème qu’on peut identifier avec le lazy loading est le fait
qu’en passant d’un module à un autre on aura toujours un chargement
des nouveaux modules.
 Si vos modules sont très volumineux ou que la connexion du client
est mauvaise, il y aura plusieurs latences. Ceci va provoquer un
problème avec l’utilisateur.
 La question qui se pose est : Y a-t-il un moyen de personnaliser les
stratégies de chargement ???

573
Preloading Lazy Loading
 La méthode forRoot de votre RouterModule prend en second
paramètre un objet vous permettant de configurer la stratégie de
chargement avec la propriété preloadingStrategy.
Cette propriété prend en paramètre, par défaut NoPreloading.
 La deuxième valeur qu’elle peut prendre est PreloadAllModules.
Elle demande à Angular de précharger tous les lazyLoaded
Modules une fois le Module principal chargé.
import { PreloadAllModules } from "@angular/router";
@NgModule({
imports: [[Link](routes, {
preloadingStrategy: PreloadAllModules
})],
exports: [RouterModule],
}) 574
Preloading Lazy Loading
Créer votre propre startégie
 Avec PreloadAllModules, tous les modules sont préchargés, ce qui peut
en fait créer un goulot d'étranglement si l'application a un grand nombre
de modules à charger.
 Une meilleure stratégie serait de charger sélectivement les modules
requis au démarrage. Par exemple, module d'authentification, module
principal, module partagé, etc.
Pour précharger sélectivement un module, nous devons utiliser une
stratégie de préchargement personnalisée.
Créez d'abord une classe qui implémente l’interface PreloadingStrategy.
La classe doit implémenter la méthode preload().
C’est cette méthode qui détermine s'il faut précharger le module ou non.

575
Preloading Lazy Loading
Créer votre propre startégie
 La signature de la fonction preload prend en paramètre un objet Route
représentant la route ciblé et en deuxième paramètre une fonction load qui retourne
un Observable.
Si vous retourne la méthode load, le module sera préchargé.
Si vous ne voulez pas le précharger retourner un Observable de null.

@Injectable({providedIn: 'root'})
export class CustomPreloadingStrategy implements PreloadingStrategy {
preload(route: Route, load: () => Observable<any>): Observable<any> {
if ([Link]["preload"]) {
return load();
}
else {
of(null);
}
}
} 576
Lazyload Standalone Component
 Vous pouvez faire du lazy loading pour les standalone component
avec la clé loadComponent.

{
path: 'track',
loadComponent: () => import('./track-by/[Link]')
.then(c => [Link])
}

577
Angular
Optimisation
AYMEN SELLAOUTI

578
Architecture de la couche Vue
 Lors de la construction d'une application Angular, l'une des questions les
plus fréquentes auxquelles nous sommes confrontés dès le début est :
comment structurons-nous notre application ?
 Une première réponse est : Arbre de composant.
 Cependant, d’autres questions se posent :
Est-ce que tous les composants sont pareils?
Comment les composants doivent-ils interagir ?
Devons nous injecter des services dans n'importe quel composant
?
Comment puis-je rendre mes composants réutilisables dans toutes les vues ?
579
Architecture de la couche Vue
Smart Components vs Presentational
Components
Un design pattern répond à ces questions en divisant les composants
essentiellement en deux types
 Composants intelligents : également appelés parfois composants
applicatif ou composants conteneur.
 Composants de présentation : également appelés parfois
composants purs ou composants muets

580
Architecture de la couche Vue
Smart Components vs Presentational Components
Presentational Component
 Ce composant a uniquement pour rôle comme son nom l’indique, la
présentation des données sans avoir d’informations sur la source de ces
derniers.
 Ceci vous permet d’avoir des composants indépendants et
réutilisables.
 Afin de paramétrer ce type de composant, utiliser le @Input
 Afin d’informer d’une action ou envoyer les données modifiées, utiliser
le @Output.
 Essayer d’utiliser ce type de composant autant que vous le pouvez.

581
Architecture de la couche Vue
Smart Components vs Presentational Components
Smart Component
 Ces composants sont smarts. Ils gèrent donc la partie
fonctionnelle.
 Ces composants sont chargés d'interagir avec la couche de service
et de récupérer les données.
 Ils sont aussi chargés de transmettre aux composants de
présentation les informations nécessaires.
 Ils doivent aussi gérer les données et les évènements envoyés par
les Presentational Component.

582
Change Detection
C’est quoi ?
 Change Detection est l’un des mécanisme les plus importants dans
Angular.
 Il permet de suivre les changements d’état de votre application et
d’afficher les modifications dans votre vue.
 Il garantit que l’interface utilisateur suit toujours d’une façon
synchrone l’état interne de votre application.

583
Change Detection
Chaque composant dans Angular est représenté par une structure
appelé View. Elle contient entre autre l’instance de la classe
Composant appelée componentInstance ainsi que la liste des
éléments du DOM représentant le Template.

[Link] 584
Change Detection
Ensuite, quand le compilateur traite le composant, il identifie les
élément qui nécessite un changement lors du changement d’état.

585
Change Detection
 Pour chacun de ces éléments, il crée des objets Bindings. C’est une
structure de données qui informe sur deux choses :
Que voulons nous mettre à jour dans le Dom
Ou récupérer la nouvelle valeur

586
Change Detection
Ensuite, dès qu’un change
Detectin est déclenché,
Angular va parcourir
l’ensemble des Views
(Component) et évaluer la
nouvelle expression du
Binding et la comparer à la
précédente.
Si la valeur est modifiée, elle
met à jour le DOM.

587
Change Detection

588
Change Detection
Quand déclencher un Change Detection
 Un Change Detection est déclenché dans ces cas d’utilisation
1. Initialisation des composants. Par exemple, lors du lancement d'une
application angular, Angular charge le composant principal et
déclenche [Link]() pour appeler la détection de
changement et le rendu de la vue.
2. Les event listener du DOM peuvent mettre à jour les données dans
un composant Angular et déclencher le Change Detection.
3. Les requêtes HTTP.

[Link] 589
Change Detection
Quand déclencher un Change Detection
4. Les MacroTasks, tells que setTimeout() ou setInterval(). En effet
vous pouvez mettre à jour les données dans la callback function d’une
macroTask comme setTimeout().
5. Les MicroTasks, comme [Link]() dont les callback peuvent
mettre à jour les données.
6. D’autres opérations asynchrones qui peuvent mettre à jour vos
données telles que [Link]() et [Link]().

[Link] 590
Change Detection
Quand déclencher un Change Detection
 La liste précédente contient les scénarios les plus courants dans lesquels
l'application peut modifier les données.
 Angular exécute le Change Detection à chaque fois qu'il détecte la possibilité
d’un changement de données.
 Le résultat de la détection des changements est que le DOM est mis à jour avec
de nouvelles données.
 Angular détecte les changements de différentes manières. Pour l'initialisation
des composants, Angular appelle explicitement la détection des
changements.
Pour les opérations asynchrones, Angular utilise une zone pour détecter les
changements aux endroits où les données auraient pu muter et exécute
automatiquement la détection des changements.
591
Change Detection
Zones et contexte d’exécution
 Afin de détecter les éléments susceptible de déclencher
un Change Détection, Angular utilise [Link].
 [Link] peut suivre et intercepter les tâches asynchrones.
 Une zone a généralement 3 phases :
 Elle commence dans un état stable
 Elle devient instable lorsque une tâche est déclenchée dans la zone
 Elle redevient stable lorsque les taches sont finalisées.
 Angular suit plusieurs API de navigateur de bas niveau au démarrage
pour pouvoir détecter les changements dans l'application
[Link] 592
VBOaoVk1E9fTBZdq1CfnRa2TtL_Kb&index=12&ab_channel=StepanSuvorov
Change Detection
[Link] c’est quoi ?
//Comment savoir quand est ce que le setTimeout commence et quand ca se termine
//sans toucher le setTimeout
const oldSetTimeout = setTimeout;
setTimeout = (handler, timer) => {
[Link]('START');
oldSetTimeout(_ => {
handler();
[Link]('FINISH');
}, timer);
}
//----------------------------------------------------------------
setTimeout(_ => {
[Link]('some action');
}, 3000);

[Link]
593
VBOaoVk1E9fTBZdq1CfnRa2TtL_Kb&index=12&ab_channel=StepanSuvorov
Change Detection
Zones et contexte d’exécution
 Ceci est donc délégué à [Link] qui suit les APIs comme
EventEmitter, DOM event listeners, XMLHttpRequest, l’API fs dans
[Link] et plus encore.
 Donc si zoneJs détecte un des déclencheur du Change Detection,
elle notifie Angular qui lui va déclencher le processus de Change
Detection.
 Angular utilise sa propre zone appelée NgZone.
 C’est une seule zone et le Change Detection est uniquement déclenché
si une opération Asynchrone est déclenchée dans cette zone.

594
Change Detection
Zones et contexte d’exécution
// [Link]
// ngzone simplified
this._onMicrotaskEmptySubscription = this._zone.[Link]({
function onEnter() { next: () => {
_nesting++; this._zone.run(() => {
} [Link]();
function onLeave() { });
_nesting--; })
checkStable();
}
function checkStable() {
if (zone._nesting == 0 && ![Link]) {
[Link](null);
}
}
//[Link]

[Link] 595
VBOaoVk1E9fTBZdq1CfnRa2TtL_Kb&index=12&ab_channel=StepanSuvorov
Change Detection
 Il existe deux stratégies de Change Detection :
 La stratégie par défaut
 La stratégie OnPush

596
Change Detection
La stratégie par défaut
 Dans la stratégie par défaut, le mécanisme est le suivant :
1. NgZone détecte une possibilité de modification.
2. Angular est notifié afin de déclencher le change detection.
3. La détection de changement vérifie chaque composant dans
l'arborescence des composants de haut en bas pour voir si le modèle
correspondant a changé. Ceci est appelé dirty checking.
4. S'il y a une nouvelle valeur, il mettra à jour la partie correspondante
(DOM)

597
[Link] 598
Change Detection
La stratégie par défaut
 Ici, dans tous les composants de
l’arbre de composant, le change
detector alloué à chaque
composant, compare la valeur
courante et la valeur précédente des
propriétés.
Si la valeur change, il va marquer
une propriété isChanged à true.

599
Change Detection
La stratégie par défaut
 Cette première stratégie est assez
performante pour les application
petite et moyenne.
Ceci est du au fait qu’Angular est très
rapide pour le Change détection pour
chaque composant en utilisant la
technique du inline-caching.
Cependant, ceci peut ne plus suffire
pour les application assez volumineuse.

[Link] 600
Change Detection
La stratégie OnPush
 Le but de cette stratégie est d’éviter les
test non nécessaires pour un composant
et sa descendance.
 En appliquant cette stratégie, on définit
une nouvelle façon pour le déclenchement
du Change Detection pour ce composant

601
Change Detection
La stratégie OnPush
Afin d’activer la stratégie OnPush, il suffit d’ajouter la propriété
changeDetection du @Component object et de lui affecter la valeur
OnPush de l’objet ChangeDetectionstrategy.

@Component({
selector: 'app-list',
templateUrl: './[Link]',
styleUrls: ['./[Link]'],
changeDetection: [Link]
})

602
[Link] 603
Change Detection
La stratégie OnPush
En utilisant cette stratégie, Angular sait
que le composant n'a besoin d'être mis
à jour que si :
 La référence d'entrée d’un Input du
composant est modifié
 Le composant ou l'un de ses enfants
déclenche un événement.
 La Change Detection est déclenchée
manuellement
 Un observable lié au Template via le pipe
async émet une nouvelle valeur.

604
Change Detection
La stratégie OnPush
Les actions suivantes ne déclenchent pas le Change Detection dans ce
contexte :
setTimeout
setInterval
[Link]().then(), [Link]().then()
[Link]('...').subscribe() (En générale, n’importe quelle inscription à
un Observable RxJs)

605
Change Detection
La stratégie OnPush
Le changement de la référence Input
Dans la stratégie de détection de changement par défaut, Angular
exécutera le détecteur de changement chaque fois que les données
@Input() sont changées ou modifiées.
En utilisant la stratégie OnPush, le détecteur de changement n'est
déclenché que si une nouvelle référence est transmise en tant que
valeur @Input().
Les types primitifs tels que les nombres, les chaînes, les booléens, null et
indéfini sont passés par valeur.

606
Change Detection
La stratégie OnPush
Le changement de la référence Input
L'objet et les tableaux sont également passés par valeur, mais la
modification des propriétés d'objet ou des entrées de tableau ne crée
pas de nouvelle référence et ne déclenche donc pas la détection de
changement sur un composant OnPush.
 Pour déclencher le détecteur de changement, vous devez passer une
nouvelle référence d'objet ou de tableau à la place.
[Link] facilite l’utilisation de l’Immutabilité.
Il fournit des structures de données immuables persistantes pour les objets
(Map) et les listes (List).

[Link] 607
[Link] 608
[Link] 609
PageComponent est OnPush
AppComponent passe une input à Page Component

[Link] 610
PageComponent et BredCrumbComponent sont OnPush
AppComponent passe une input à Page Component

[Link] 611
Tous les composants sont OnPush

[Link] 612
Tous les composants sont OnPush
GalleryComponent A un pipe Async et elle passe le résultat en
Input à SliderComponent

[Link] 613
Optimiser une application
Change Detection - La stratégie OnPush
Out of Bound Change Detection
 Ce problème se produit lorsqu’une action qui modifie uniquement
l’état local d’un composant déclenche des changes detection dans
des parties qui n’ont aucun rapport avec cette action.
 Solution : Utiliser OnPush et considérer du refactoring de votre
composant.

614
Change Detection
La stratégie OnPush
Le changement de la référence Input
Optimisation
Pensez à externaliser les parties ou vous avez des évènement et le
component recevant le @Input.
 Ceci va faire en sorte que le composant avec le @Input ne sera réaffiché
que lorsqu’il aura une nouvelle référence.

615
Optimiser une application
Recalculation of referentially transparent
expressions
 Cette problématique est soulevée lorsque vous avez une expression dans
votre Template qui doit être recalculé même lorsque ses paramètres ne
changent pas.

616
Change Detection
Recalculation of referentially transparent
expressions
Optimisation
Pensez à utiliser des pipes pour vos calcules.
 Il faut avoir deux conditions :
 Pas d’effets de bords (side effect), vous n’appelez pas d’api, pas de console,

 Se sont des opérations pures, si l’entrée ne change pas, le résultat ne change
pas il reste le même
Pourquoi recalculer alors => utiliser les pipes, si l’entrée ne change pas, il ne
recalculera pas l’opération.

617
Change Detection
Recalculation of referentially transparent
expressions
Optimisation
 Vous pouvez alors aller encore plus dans l’optimisation, puisque ce sont
des fonctions pures. Si une fonction a été déjà calculée, pourquoi le
refaire même si ce n’est pas la même entrée.
 Pensez à utiliser le concept de cache avec le memo-decorator.
 importer le décorateur memo de la bibliothèque memo-decorator et
appliquer le à votre fonction transform.
npm i memo-decorator

618
Change Detection
Le changement déclenché manuellement
 Par défaut c’est [Link] à travers NgZone qui gère la partie déclenchement
du change detection.

[Link] 619
Change Detection
Le changement déclenché manuellement

620
Change Detection
Le changement déclenché manuellement
 Dans certains cas d’utilisation, ce mode de fonctionnement peut
causer des problèmes et ralentir l’application.
 Prenons le cas des évènements fréquents qu’on combinera avec des
Change Detection ayant beaucoup de calcul :
 mousemove,
 des scroll,
 un setInterval avec des interval courts.

621
Change Detection
Le changement déclenché manuellement
Zone Pollution Pattern
 Ce problème est identifié comme « Zone Pollution Pattern »
 Il se produit lorsque Angular Zone encapsule un callback qui déclenche
des détection de changement redondants.
 La solution est donc de déplacer la logique d’initialisation à
l’extérieur de Angular Zone
Injecter NgZone
Appeler la méthode runOutsideAngular
Passez y une callback qui effectue le traitement que vous voulez
isoler.

622
Change Detection
Le changement déclenché manuellement
Zone Pollution Pattern

[Link] 623
Change Detection
Le changement déclenché manuellement
Zone Pollution Pattern
 Vous pouvez aussi désactiver complétement la prise en main de [Link] et
tout faire vous-même.
platformBrowserDynamic().bootstrapModule(AppModule , [
{ngZone: 'noop'}
])

constructor(private applicationRef: ApplicationRef) {


[Link]();
}

[Link] 624
Change Detection
Le changement déclenché manuellement
Il existe trois méthodes pour déclencher manuellement les détections de
changement :
La méthode tick() de ApplicationRef qui déclenche la détection de
changement pour l'ensemble de l'application en respectant la stratégie
de détection de changement d'un composant

625
Change Detection
Le changement déclenché manuellement
 Il existe trois méthodes pour déclencher manuellement les
détections de changement :
 detectChanges() du ChangeDetectorRef qui exécute
la détection de changement sur cette vue et ses enfants
en gardant à l'esprit la stratégie de détection de changement.
 Il peut être utilisé en combinaison avec detach() pour
implémenter des contrôles de détection de changement
locaux.

626
Change Detection
Le changement déclenché manuellement
Il existe trois méthodes pour déclencher manuellement
les détections de changement :
markForCheck() de ChangeDetectorRef qui ne
déclenche pas la détection de changement mais
marque tous les ancêtres OnPush comme devant être
vérifiés une fois, soit dans le cadre du cycle actuel ou du
cycle de détection de changement suivant. Il exécutera la
Change Detection sur les composants marqués même
s'ils utilisent la stratégie OnPush.

627
Change Detection
Détacher un composant du change detection
 Vous pouvez détacher un composant complétement
du Change Detection.
 Ceci peut être fait pour les composants qui n’ont pas
d’état et donc pas besoin que le ChangeDetector agisse.
 Vous avez beaucoup de calcule lourd et vous voulez
gérer seul le déclanchement du Change Detection
localement.
Vous pouvez rattacher le composant quand c’est
nécessaire.

[Link] 628
Change Detection
Détacher un composant du change detection

629
Exercice
 Clone ce projet :
[Link]
 Lancez votre projet, c’est le RhComponent qui est exécuté.
 Analysez ses problèmes et essayez de les résoudre avec les techniques
étudiées.

630
Notes
1. Zone Pollution Pattern
2. Out of Bound Change Detection
3. OnPush Stratégie
4. Recalculation of referentially transparent expressions

631
Angular
State Management
NgRx Store
AYMEN SELLAOUTI

632
NgRx
Introduction
Dans la plupart de nos
applications, nous gérons des
Model
composants qui (Services) Component
communiquent avec des
models afin de gérer leur
état. Model
Component
(Services)
Plus l’application grandi et
plus ca devient compliqué de
Model
gérer l’état de l’application (Services) Component
qui est éparpillé dans les
composants et les services.
633
NgRx
Introduction
Solution : Gestion centralisée de l’état de votre application

REDUX

634
NgRx
Introduction

[Link] 635
NgRx
Introduction
Redux
 REDUX est un pattern né de FLUX, une architecture créée par
Facebook.
 FLUX Se base sur un workflow de données unidirectionnel grâce
à un dispatcher, qui recueille des actions distribuées par le serveur ou
par l’utilisateur.
REDUX est une version moins complexe de Flux. Il se distingue
par :
 Une source de vérité unique : le store ;
 Des états immuables
 Pas de dispatcher vue qu’on a un seul store
636
NgRx
Introduction
Redux Vs Flux

[Link] 637
NgRx
Introduction
Redux Vs Flux

[Link] 638
NgRx
Introduction
Redux
Store : C’est un objet contenant le state de votre application. Le
store n’est pas le state.
 State : C’est la source de vérité unique. C’est un objet
immutable qui centralise l’état de votre application.
L’état est la représentation de votre application à un moment
donnée.
 Les changement de l’état sont faites par des fonctions pures. Ces
fonctions n’ont aucun effet de bord et ne modifient aucune
donnée. Elle ne font que retourner une nouvelle valeur de l’état.
 Les fonctions pures ne se basent que sur les paramètres d’entrées
et ne gèrent pas les éléments externes. Ceci implique que pour
une même entrée, on aura toujours le même résultat de sortie.
[Link] 639
NgRx
NgRx est une implémentation du pattern Redux.
Elle permet de gérer l’état de votre application.
NgRx est un framework pour créer des applications réactives dans
Angular. NgRx fournit des bibliothèques pour:
 Gérer l'état global et local de votre application.
 Isoler des effets de bord permettant d’avoir une architecture de
composants plus propre.
 Gérer la collection de vos entités.
 S’intégrer avec le routeur d’Angular.

640
NgRx
Quand L’utiliser
NgRx présente plusieurs avantages, cependant il ne faut pas
toujours l’utiliser.
 Dans la documentation officielle de NgRx, on vous demande
de réfléchir avant d’entamer l’utilisation de NgRx dans votre
application.
 L’idée est de ne pas ajouter une couche de complexité à
votre code si vous n’en avez pas besoin.

[Link] 641
NgRx
Quand L’utiliser
Le principe à suivre s’appelle SHARI. Il permet de voir quand utiliser NgRx :
 Shared: Est ce que votre State est partagé par plusieurs composants et
services ?
 Hydrated: Est ce que votre State doit être conservé et hydraté lors des
recharges de pages
 Available: Est ce que le State doit être récupéré quand vous changer de
routes.
 Retrieved: Est ce que votre State doit être récupéré à travers des effets de
bord, e.g. une requête HTTP.
 Impacted: Est ce que le State est impacté par plusieurs components

[Link] 642
NgRx
Quand L’utiliser
Voici les cas d’usages les plus présents :
 Quand plusieurs composants et services dépendent de la même portion du state

Chaque fois que vous refactorisez votre composant en plusieurs morceaux, vous
aurez une énorme probabilité d'introduire un système de gestion d'état.

Imaginez que vous ayez une grosse application avec beaucoup de pagination de
filtres, etc. Si vous quittez votre page pour modifier quelque chose à la page 5 et que
vous revenez ensuite, si vous êtes dans l'état initial de la page (page1 et pas de filtre)
c'est vraiment une mauvaise expérience. Dans ce cas, un système de gestion d'état
qui gère tous vos filtres, pagination, etc. vous aidera à conserver l'état.

[Link] 643
NgRx
Quand L’utiliser
 D’autre part si on revient à la création de Flux et la première
apparition de Redux, les problèmes majeurs qu’avait à gérer la team
Facebook sont :
 Plusieurs acteurs modifient les données, généralement c’est le
serveur et le client.
 Le problème de ‘Extraneous props’ ou d’@Input étrangers.
C’est-à-dire qu’on trouve dans les composants des propriétés qui
n’ont rien à voir avec l’état du composant mais qui servent juste à
récupérer des paramètres à passer aux composant fils ou père.

644
NgRx
Quand L’utiliser
[Link]
ngconf-2020-part-1-introduction-h8l
[Link]
ngularConnect
[Link]
behaviorsubject-4818
[Link]

645
NgRx
Architecture

[Link] 646
NgRx
Architecture

[Link] 647
NgRx
Architecture

Smart Component Presentational Component


Se préoccupe du Store Ne se préoccupe pas du store
Dispache les actions Invoque les callbacks via les @Output
Récupère les données du Store Lit les datas des @Input

648
NgRx
Architecture

649
NgRx
Installation
Vous pouvez installer cette bibliothèque vous pouvez utiliser
les commandes suivantes :
 npm install @ngrx/store providers: [
provideRouter(routes),
 yarn add @ngrx/store provideStoreDevtools({ maxAge: 25, logOnly: !isDevMode() }),
provideStore(),
 ng add @ngrx/store provideEffects(),
L’utilisation de la commande
] ng add déclenchera les shcematics
installant la bibliothèques et ajoutant les fichiers et la configuration
nécessaire.
Le store sera configuré directement via la méthode provideStore

650
NgRx
Installation / debugger
Vous pouvez débugger votre application avec le redux store devtools.
Il faut d’abord installer l’extension Redux DevTools
[Link]
Pour l’installer lancer la commande suivante :
ng add @ngrx/store-devtools
providers: [
provideStoreDevtools({ maxAge: 25, logOnly: !isDevMode() }),
provideStore(),
]

[Link] 651
NgRx
Architecture

652
NgRx
Store
 Afin de récupérer votre store, vous devez l’injecter.
 Une fois fait, le store est représenté par un
observable qui n’offre aucune méthode permettant de
manipuler le state.
 Etant un objet générique, vous pouvez lui spécifier
l’objet représentant votre état (le state).
export interface AppState {}
[Link]
constructor(
private store: Store<AppState>
)

653
NgRx
Store API
 dispatch(action: Action) : cette méthode permet de déclencher une
action pour mettre à jour l'état de l'application. Elle prend en paramètre
un objet de type Action décrivant son type et les données associées.
 pipe(select(selector: any)) : cette méthode permet de sélectionner
une partie de l'état de l'application à partir d'un sélecteur. Elle
retourne un Observable qui émet les valeurs de l'état sélectionné.
subscribe() : cette méthode permet de s'abonner aux changements
de l'état de l'application. Elle prend en paramètre des fonctions
callback pour gérer les changements de l'état, les erreurs et la fin de
l'abonnement.
654
NgRx
Architecture

655
NgRx
Actions
 Les actions sont l'un des principaux composants de NgRx.
 Les actions expriment des événements uniques qui se produisent
dans votre application.
 Que ce soit des événements internes de votre utilisateur, ou des
événements externes via le réseau ou tout autre événement, les actions
sont la pour les décrire. interface Action {
type: string;
 Les actions NgRx doivent implémenter l’interface Action. }

 La propriété type représente le type qui est l’identifiant de l’action.


 Vous disposez aussi d’une propriété payload qui contiendra les
informations à fournir en cas de besoin avec votre action
[Link] 656
NgRx
Actions
 La propriété type suit une convention de nommage : [Source]
Event
 Ceci permet de définir le contexte et de spécifier quelle catégorie
d'action il s'agit et d'où une action a été dispatchée.
'[Login Page] User Login'

 Vous ajoutez des propriétés à une action pour fournir un contexte ou


des métadonnées supplémentaires pour une action. {
type: '[Login Page] User Login',
username: string;
password: string;
}

[Link] 657
NgRx
Actions
NgRx nous fournit une import { createAction, props } from "@ngrx/store";
méthode createAction qui vous import { User } from './model/[Link]';
permet de créer une action et const enum ActionsEnum {
une fonction props qui permet 'LOGIN' = '[Login Page] User Login'
}
de spécifier le type du payload
donnant plus de robustesse à export const loginAction = createAction(
votre code. [Link],
props<{user: User}>()
)

[Link] 658
NgRx
Actions
import { createAction, props } from "@ngrx/store";
import { User } from './model/[Link]';
const action = createAction('[Source] action simple');
action();
const action = createAction('[Source] action simple', props<User>());
action({
id: "1",
name: "aymen",
email: "aymen@[Link]",
});
const action = createAction('[Source] action simple',(user: User, prefix: string) => ({
...user,
name: `${prefix}${[Link]}`,
}) );
const user: User = { /* ... */ };
action(user, '@');

[Link] 659
NgRx
Actions
 Afin de déclencher une action, [Link](loginAction({user}));
vous devez utiliser la méthode
dispatch de votre store.
 Cette méthode prend en
paramètre une action.
 En dispatchant cette action, on
informe le store qu’un événement
s’est déclenché et qu’il faut notifier
les reducers et les ‘effects’.

[Link] 660
NgRx
Actions
@Component({
selector: 'app-add-customer',
templateUrl: './[Link]',
styleUrls: ['./[Link]'],
})
export class AddCustomerComponent {
constructor(public store: Store<AppState>) {}
name = '';
email = '';
addCustomer() {
const newCustomer = new Customer(uuidv4(), [Link], [Link]);
[Link](
[Link]({ customer: newCustomer })
); addCustomerAction
} export interface AppState {
} customers: Customer[];
}
661
NgRx
Actions
Bonne pratiques
Il existe quelques règles pour écrire de bonnes actions dans votre
application.
Commencer par les actions : écrivez des actions avant de
développer des fonctionnalités pour avoir une vue globale de ce que
vous aller implémenter.
Catégoriser : catégorisez les actions en fonction de la source de
l'événement.
Soyer généreux en actions : les actions sont peu coûteuses à écrire,
donc plus vous écrivez d'actions, mieux vous exprimez les flux dans
votre application.
[Link] 662
NgRx
Actions
Bonne pratiques
 C’est une bonne pratique de définir vos actions à coté des
fonctionnalités qui les utilisent

[Link] 663
NgRx
Actions
Les groupes d’actions (Actions Groups)
 Vu que le groupement des actions de même sources est une
bonne pratique, NgRx à partir de la version 13 offre la possibilité de le
faire via les ActionGroup.
 Pour ce faire, utiliser la fonction createActionGroup.
 Elle prend en paramètre un objet avec deux propriétés à renseigner:
 source : qui représente la source et qui sera ajouté automatiquement à
toutes les actions du groupe.
 events : qui est un objet contenant les actions du groupe avec pour clé
le nom de l’action (il sera transformé en camelCase pour récupérer l’action)
et pour valeur la props si elle existe sinon la fonction emptyProps.

[Link] 664
NgRx
Actions
Les groupes d’actions (Actions Groups)
export const exampleActions = createActionGroup({
source: “Group Actions",
events: {
"Load Users": emptyProps(),
"Add User": props<{ user: User }>(),
},
});

[Link]([Link]({ user }));

[Link] 665
NgRx
Action
Nouveau State
Reducer
State
Reducers
 Un Reducer est une fonction JS pure.
 Elle est appelée suite à un évènement (une Action) et reçoit en
paramètre l’état actuel de l’application (le state) ainsi que l’action
dispatché par le store (qui contient le payload s’il existe).
 En fonction du type de l’action et de son payload, le reducer
retourne le nouvel état (state) de l’application (du store).
 Les reducers doivent garder l’immutabilité du state, ceci nous
permet d’avoir un historique des différentes versions de l’état de
l’application.

[Link] 666
NgRx
Reducers
 Afin de créer un reducer et à partir de la version 8 de NgRx, vous
pouvez utiliser le helper createReducer.
 createReducer prend en paramètre l’état initial du state, et la
fonction à déclencher pour une action donnée.
 Dans les anciennes versions de RxJs, le travail se faisant avec un
switch qui selon le type de l’action exécutait le traitement nécessaire.
 A partir de la version 8 vous pouvez utiliser le helper on qui prend en
premier paramètre l’action à gérer et en second paramètre la fonction à
exécuter. Cette fonction reçoit le state actuel et l’action et retourne
le nouveau state.
[Link] 667
NgRx
Reducers export function reducer(
state = initialState,
action: [Link]
): State {
const scoreboardReducer = createReducer( switch ([Link]) {
initialState, case [Link]: {
on([Link], return { ...state, home: [Link] + 1,};
state => ({ ...state, home: [Link] + 1 }) }
), case [Link]: {
on([Link], return {...state, away: [Link] + 1,};
state => ({ ...state, away: [Link] + 1 }) }
), case [Link]: {
on([Link], return [Link];
state => ({ home: 0, away: 0 })), }
on([Link], default: {
(state, { game }) => ({ home: [Link], away: [Link] })) return state;
); }
}
}

[Link] 668
NgRx
Reducers
export interface AuthState {
 Pensez pour chaque reducer à user: User;
définir une interface décrivant la }
portion du state qu’il gère. export const initialAuthState: AuthState = {
user: null,
 Définissez le state initiale de };

cette portion. export const authReducer = createReducer(


initialAuthState,
 Exportez maintenant votre on([Link], (state, action) => {
reducer et définissez le au niveau du return {
user: [Link],
store module. };
})
);

[Link] 669
NgRx
Reducers (Root State)
Enregistrement de l’état principale
L'état de votre application est défini comme un seul grand objet.
L'enregistrement des reducers pour gérer des parties de votre état vous
permet de définir uniquement les clés avec des valeurs associées dans l'objet.
Pour enregistrer le Store global dans votre application, utilisez la méthode
provideStore.
Elle prend un objet avec comme clé l’identifiant du reducer et comme
valeur le reducer. En enregistrant le state avec, vous rendez le state
disponible dès le lancement de l’application.
providers: [
provideStoreDevtools({ maxAge: 25, logOnly: !isDevMode() }),
provideStore({ app: reducer}),
]
[Link] 670
NgRx
Reducers (Feature State)
Enregistrement de l’état des fonctionnalités
Les états fonctionnels (Feature State) se comportent de la même
manière que l’état principal.
 Chaque partie fonctionnelle aura son état qu’elle va gérer mais qui sera
aussi une partie de l’état principal.
 Pour les états fonctionnels, utilisez la méthode provideState qui
prend aussi un objet avec l’identifiant de la portion et le reducer.
providers: [
provideStore({ app: reducer}),
provideState({ name: "todo", reducer: todoReducer }),
provideStoreDevtools({ maxAge: 25, logOnly: !isDevMode() }),
],

[Link] 671
NgRx
Reducers (Feature State)
Enregistrement de l’état des fonctionnalités

[Link]("auth", authReducer),

[Link] 672
NgRx
Reducers
Bonnes pratiques
 Créer un reducer par fonctionnalité
 Seul les reducers peuvent modifier l’état QUI DOIT RESTER
IMMUTABLE.
 Evitez de développer dans le reducer directement. Créer des helpers
et appeler les.

[Link] 673
NgRx
Sélectionner des éléments du store
 Afin de récupérer les données de votre state vous pouvez utiliser
plusieurs méthodes.
 Le Store que vous utilisez pour dispatcher des actions vous permet
aussi d’accéder au state global de votre application.
 Le store étant un Observable, vous pouvez vous y inscrire et
récupérer le state de l’application.

[Link]((state) => {
[Link]("state : ", state);
[Link]("autState : ", state['auth']);
});

[Link] 674
NgRx
Sélectionner des éléments du store
 Afin de sélectionner la partie que vous voulez, utilisez
l’opérateur map de rxjs. [Link]$ = [Link](
map((state) => !! state["auth"].user)
);
 Le problème ici est que cette opération va
se répéter à chaque fois alors que le résultat [Link]$ = [Link](
risque d’être le même. Une fonction pure qui map(
a le même input retournera toujours le même (state) => state["auth"]
),
Output. distinctUntilChanged()
);
 La Solution est donc d’utiliser l’opérateur de
RxJs distinctUntilChanged.
[Link] 675
NgRx
Sélectionner des éléments du store
select operator
 Afin de nous aider dans cette démarche, NgRx nous offre l’opérateur
select qui réalise un map selon une fonction pure et ne déclenche le flux
de l’observable qui si le résultat change en utilisant
distinctUntilChanged.
 Elle prend en paramètre le state et elle applique une fonction de
mapping.
[Link]$ = [Link](
(state) => !!state["auth"].user
);

[Link] 676
NgRx
Sélecteurs
 Le problème avec le select c’est qu’il effectue à chaque fois l’opération
de map. Dans certains cas, elle peut être très couteuse, sa répétition pose
donc un problème de performance.
 Le Store fournit la fonction createSelector afin d’optimiser cette sélection.
En effet ce helper permet d’avoir de la :
 Portabilité
 Mémorisation
 Composition
 Testabilité
 Sécurité de type ( Type Safety)
[Link] 677
NgRx
Sélecteurs
 Lors de l'utilisation de createSelector, NgRx garde la trace des derniers
arguments avec lesquels votre fonction de sélection a été appelée.
 Étant donné que les sélecteurs sont des fonctions pures, le dernier
résultat peut être renvoyé lorsque les arguments correspondent sans ré
invoquer votre fonction de sélection.
Cette pratique est connue sous le nom de mémorisation.
 Un sélecteur est donc tout simplement une fonction de mapping avec de la
mémoire.
 createSelector retourne un objet de type MemoizedSelector.
[Link] 678
NgRx
Sélecteurs
import { createSelector } from '@ngrx/store';
 La fonction createSelector prend
en paramètre un ensemble de export interface FeatureState {
sélecteurs (8 maximum) permettant counter: number;
}
de sélectionner ce dont vous avez
besoin pour mapper votre state suivi export interface AppState {
feature: FeatureState;
de la fonction de map qui est }
toujours le dernier paramètre.
export const selectFeature = (state: AppState) => [Link];
 La fonction de map récupère
comme paramètres le résultat de export const selectFeatureCount = createSelector(
selectFeature,
l’ensemble des sélecteurs passé en (state: FeatureState) => [Link]
paramètre avec elle. );

[Link] 679
NgRx
Sélecteurs
import { createSelector } from '@ngrx/store'; export const selectUser = (state: AppState) => [Link]
er;
export interface User { export const selectAllBooks = (state: AppState) => [Link]
id: number; s;
name: string;
} export const selectVisibleBooks = createSelector(
selectUser,
export interface Book { selectAllBooks,
id: number; (selectedUser: User, allBooks: Book[]) => {
userId: number; if (selectedUser && allBooks) {
name: string; return [Link]((book: Book) => [Link] === select
} [Link]);
} else {
export interface AppState { return allBooks;
selectedUser: User; }
allBooks: Book[]; }
} );

[Link] 680
NgRx
Sélecteurs de fonctionnalités
featureSelector
 Afin de centraliser et de typer la partie de votre state qui correspond à une
fonctionnalité particulière (généralement le state du module), vous pouvez
utiliser les featureSelector.
 Pour ce faire, utilisez la méthode createFeatureSelector caster la au
fetaureStateType que vous souhaitez et passer lui comme paramètre la clé
représentant la partie du state que vous voulez utiliser.
export const selectUser = (state: AppState) => [Link];

export const authFeatureSelector = createFeatureSelector<AuthState>("auth");

[Link] 681
NgRx
Sélecteurs de fonctionnalités
createFeature
Il existe trois éléments principaux de la gestion globale de l'état avec
@ngrx/store : les actions, les réducteurs et les sélecteurs.
 Pour un état de fonctionnalité particulier, nous créons un réducteur pour
gérer les transitions d'état en fonction des actions et des sélecteurs distribués
pour obtenir des tranches de l'état de fonctionnalité.
 Nous devons également définir un nom de fonctionnalité nécessaire pour
enregistrer le réducteur de fonctionnalités dans le magasin NgRx.
 Par conséquent, nous pouvons considérer la fonctionnalité NgRx comme
un regroupement du nom de la fonctionnalité, du reducer de
fonctionnalité et des sélecteurs pour l'état de la fonctionnalité particulier.
 [Link] 682
NgRx
Sélecteurs de fonctionnalités
createFeature
Afin d’éviter le code répétitif généré avec la création des différents
sélecteurs, NgRx propose à partir de la version 16 la fonction createFeature
 La fonction createFeature réduit le code répétitif dans les fichiers de
sélection en générant des sélecteurs enfants pour chaque propriété
d'état de fonctionnalité.
 Elle accepte un objet contenant un nom de fonctionnalité et un reducer de
fonctionnalité comme argument d'entrée :
export const appFeature = createFeature({
name: "app",
reducer: reducer,
});

[Link] 683
NgRx
Sélecteurs de fonctionnalités
createFeature
 Afin d’ajouter vos propres selecteurs, vous pouvez utiliser l’option extraSelectors.
 L'option extraSelectors accepte une fonction qui prend les sélecteurs générés
comme arguments d'entrée et renvoie un objet de tous les sélecteurs supplémentaires.

export const todoFeature = createFeature({


name: "todo",
reducer: todoReducer,
extraSelectors: ({ selectTodos }) => {
const selectCompletedTodos = createSelector(selectTodos, (todos) =>
[Link]((todo) => [Link])
);
const selectIncompletedTodos = createSelector(selectTodos, (todos) =>
[Link]((todo) => ![Link])
);
return { selectCompletedTodos, selectIncompletedTodos };
},
});
684
NgRx
Les effects

[Link] 685
NgRx
Les effects
 Dans vos applications, vous avez tout le temps besoin d’interagir avec
les effets de bord (side effects) comme par exemple communiquer avec
une API.
 Dans les applications basées sur les services, les composants sont
responsables de l'interaction avec les ressources externes directement via
les services.
 Ceci pose un problème architectural. Nous voulons éviter cette
communication direct entre composant et service et isoler le
traitement des effets de bord.
 C’est la ou interviennent les effects.
[Link] 686
NgRx
Les effects
Les effects isolent les effets de bord des composants, permettant
d’avoir des composants plus purs qui sélectionnent les états et
déclenchent des évènements.
Les effects sont des services qui écoutent un observable de chaque
action dispatchée depuis le Store.
Les effects filtrent ces actions en fonction du type d'action qui les
intéresse. Cela se fait à l'aide d'un opérateur.
Les effets exécutent des tâches synchrones ou asynchrones et
renvoient une nouvelle action.

[Link] 687
NgRx
Architecture

688
NgRx
Les effects
Installation
Vous pouvez installer la package en utilisant npm via la commande :
npm install @ngrx/effects
A partir de la version 6 il est recommandé d’utiliser la commande add
du cli qui va aussi configurer votre application avec le module installé.
ng add @ngrx/effects@latest
Cette commande fera les fonctionnalités suivantes :
Mettre à jour [Link] dependencies avec avec @ngrx/effects.
Exécuter npm install pour installer ces dépendances.
Mettre à jour votre src/app/[Link] et le tableau de la clé imports avec
[Link]([]).
[Link] 689
NgRx
Les effects
Configuration
Pour configurer vos effects passez en paramètre de votre méthode provideEffects un
tableau des effects que vous voulez ajouter.

providers: [ export const routes: Routes = [


provideStore({ app: reducer }), {
provideState(todoFeature), path: "todo",
provideStoreDevtools({ maxAge: 25, logOnly: !isDevMode() }), component: TodoComponent,
provideEffects([]), providers: [
], provideEffects([TodoEffects]),
/* provideState("todo", todoReducer), */
],
},

[Link] 690
NgRx
Les effects
 Les effects se basent sur l’écoute sur les actions qui sont
dispatchées.
 Afin d’écouter sur les actions, NgRx vous offre le service Actions qui
étends de la class Observable (n’oublier pas NgRx est basée sur RxJs).
 Injecter le et faite le traitement nécessaire selon le type de l’action.

import { Actions } from "@ngrx/effects";

constructor(private actions$: Actions) {}

[Link] 691
NgRx
Les effects
 Maintenant que vous disposez d’un moyen d’écouter sur vos actions,
créer votre effects, NgRx vous offre le helper createEffect.
[Link]
 Cette api prend en paramètre une fonction de mapping qui selon le
type de votre action, va mapper l’observable.
 Généralement vous avez deux possibilité :
 mapper l’observable à une action qui sera traitée par le reducer
 ou faire un traitement et arrêter le process (exemple : sauvegarder le
user connecté dans le local storage).

[Link] 692
NgRx
Les effects
createEffect va retourner un nouvel Observable qui retourne une
Action. Si vous ne voulez pas le faire, ajouter l’option {dispatch: false}
 Afin de tester le type de l’action sans passer par un switch ou un if,
NgRx vous offre le helper ofType

[Link] 693
NgRx
Les effects
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { EMPTY } from 'rxjs';
import { map, mergeMap, catchError } from 'rxjs/operators';
import { MoviesService } from './[Link]';

@Injectable()
export class MovieEffects {

loadMovies$ = createEffect(() => [Link]$.pipe(


ofType('[Movies Page] Load Movies'),
mergeMap(() => [Link]()
.pipe(
map(movies => ( { type: '[Movies API] Movies Loaded Success', payload: movies } )),
catchError(() => EMPTY)))));
constructor(private actions$: Actions, private moviesService: MoviesService) {}
}
[Link] 694
NgRx
Les effects
import { Injectable } from "@angular/core";
import { Actions, createEffect, ofType } from "@ngrx/effects";
import { tap } from "rxjs/operators";
import { AuthActions } from "../ngrx/action-types";

@Injectable()
export class AuthEffects {
login$ = createEffect(
() =>[Link]$.pipe(
ofType([Link]),
tap(action => [Link]('user',[Link]([Link])))
),
{dispatch: false}
)
constructor(private actions$: Actions) {}
}

[Link] 695
NgRx
NgRx router-store
 NgRx nous offre un outil de débogage très intéressant qui est le router
store.
 Il va vous permettre de garder trace de votre router au niveau du
store et de visualiser à travers le timeline du devTools debugger les
changements au niveau de votre UI selon les actions dispatchées au
niveau de votre application.
npm install @ngrx/router-store –save
ng add @ngrx/router-store@latest

[Link] 696
NgRx
NgRx router-store
Configuration
 Afin de configurer votre module, importer le module StoreRouterConnectingModule
au niveau de votre application.
 Appeler la méthode forRoot et passez lui un objet de configuration de type
StorRoutingConfig.
stateKey: la clé de l’état du router dans le store (‘router’ par défaut)
serializer: Comment le snapshot du router est sérialisé, par défaut c’est le
DefaultRouterStateSerializer
navigationActionTiming: Quand l’action ROUTER_NAVIGATION est dispatchée
routerState: Pour décider quel sérialiser utiliser interface StoreRouterConfig {
stateKey?: string | Selector<any, RouterReducerState<T>>;
[Link]({ serializer?: new (...args: any[]) => RouterStateSerializer;
stateKey: "router", navigationActionTiming?: NavigationActionTiming;
routerState: [Link], routerState?: RouterState;
}), }
[Link] 697
NgRx
NgRx router-store
Configuration
 Maintenant vous devez spécifier quel reducer va être utilisé pour gérer les
actions dispatchées concernant votre router.

[Link]({
root: rootReducer,
router: routerReducer
}, {}),

[Link] 698
NgRx
NgRx router-store
NgRx Store DevTools

[Link] 699
NgRx
NgRx router-store
selectors
 Afin de sélectionner les éléments de votre router-store, vous pouvez
utiliser les selectors offerts par l’application.
import { getRouterSelectors, RouterReducerState } from "@ngrx/router-store";
export const {
selectCurrentRoute, // select the current route
selectFragment, // select the current route fragment
selectQueryParams, // select the current route query params
selectQueryParam, // factory function to select a query param
selectRouteParams, // select the current route params
selectRouteParam, // factory function to select a route param
selectRouteData, // select the current route data
selectRouteDataParam, // factory function to select a route data param
selectUrl, // select the current url
selectTitle, // select the title if available
} = getRouterSelectors();

[Link] 700
NgRx
NgRx runtime check
 Les runtime checks sont là pour guider les développeurs à suivre les
concepts de base et les bonnes pratiques de NgRx et Redux.
 Pendant le développement, lorsqu'une règle est violée, une erreur est
générée pour vous informer de la raison et de l'endroit où une erreur s'est
produite.
 Vous avez 6 types de runtime checks fournies avec NgRx.

[Link] 701
NgRx
NgRx runtime check
Par défaut activé:
 strictStateImmutability: vérifie que l'état n'est pas muté.
 strictActionImmutability: vérifie que les actions ne sont pas mutées
 Désactivé par défaut:
 strictStateSerializability: vérifie si l'état est sérialisable
 strictActionSerializability: vérifie si les actions sont sérialisables
 strictActionWithinNgZone: vérifie si les actions sont distribuées dans
NgZone
 strictActionTypeUniqueness: vérifie si les types d'actions enregistrés
sont uniques
[Link] 702
NgRx
NgRx runtime check
 Afin de configurer les runtime checks, aller dans la configuration de votre store principale et passez
en deuxième paramètre un objet d’options.
 Modifier la clé runtimeChecks avec un objet contenant les checks que vous voulez activer ou non.

[Link](reducers, {
runtimeChecks: {
strictActionImmutability: true,
strictActionSerializability: true,
strictStateImmutability: true,
strictStateSerializability: true
}
}),

[Link] 703
NgRx
Les méta reducers
 Les méta reducers interceptent les actions avant qu’elles arrivent au
reducers.
 Ceci va permettre un prétraitement des actions avant qu’elles n’arrivent
aux reducers. Le cas d’un loguer qui logue l’état d’un state avant l’action et
l’action en cours est illustratif du besoin des meta-reducers.
 Dans le monde Redux, les meta-reducers sont les middlwares.
 Un meta-reducer prend donc en paramètre une ActionReducer et
retourne une ActionReducer.

[Link] 704
NgRx
Les méta reducers
export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
return function(state, action) {
[Link]('state', state);
[Link]('action', action);
return reducer(state, action);
};
}
export const metaReducers: MetaReducer<any> [] = [debug];

[Link] 705
NgRx
NgRx Entity
 Généralement dans votre state vous allez sauvegarder un ensemble de
classe représentant vos entités dans la base de données.
 Le premier réflexe est de les formaliser sous forme d’un tableau.
 Ceci rend plus difficile la recherche des éléments pour des opérations
récurrentes telles que la sélection d’un élément par id.
 Afin de remédier à ca, une bonne pratique est d’utiliser la structure
map.
 Ceci nous aide dans la partie recherche mais pas dans le tri, la solution
c’est d’avoir un tableau nous permettant de gérer uniquement l’aspect tri.
 Tout ceci est déjà prêt avec la bibliothèque NgRx Entity.
[Link] 706
NgRx
NgRx Entity
 NgRx Entity fournit une API pour manipuler et interroger les
collections d'entités :
 Réduit la répétition de code pour créer des reducrs qui gèrent une
collection de modèles.
 Fournit des opérations CRUD performantes pour la gestion des
collections d'entités.
 Fournit des Adaptateurs de type sécurisé extensibles pour
sélectionner les informations d'entité.

[Link] 707
NgRx
NgRx Entity
 Afin d’installer la bibliothèque utiliser l’une des deux commandes
suivantes :

 ng add @ngrx/entity@latest

 npm install @ngrx/entity

[Link] 708
NgRx
NgRx Entity
 NgRx Entity fournit interface générique pour la manipulation générique
 ids: un tableau des clés primaries de la collection
 entities: un dictionnaire d’entités indexé par la clé primaire

interface EntityState<V> {
ids: string[] | number[];
entities: { [id: string | id: number]: V };
}

[Link] 709
NgRx
NgRx Entity
 Afin d’utiliser cette interface, faite en sorte que votre featureState
étende l’EntityState et passez lui le model que vous gérer.
export interface CvState extends EntityState<Cv>
{
error: string | null;
}
export const initialCvState: CvState = {
ids: [],
entities: {},
error: null,
};
[Link] 710
NgRx
NgRx Entity
Entity Adapter
 NgRx fournit aussi une entité Adapter
 Le rôle de l’Entity Adapter est de faciliter la gestion des Entity:
CRUD + Sélection
 Elle fournit un ensemble de méthodes permettant de gérer votre
state.
 Vous pouvez définir un adapter en utilisant la méthode
createEntityAdapter

export const adapter: EntityAdapter<User> = createEntityAdapter<User>();


[Link] 711
NgRx
NgRx Entity
Entity Adapter
 En option, la méthode createEntityAdapter prend en paramètre 2
propriétés pour la configuration.
 selectId: Une méthode vous permettant de sélectionner la clé primaire
de la collection. Cette méthode est optionnelle si vous avez un champ id.
 sortComparer: Une fonction de comparaison utilisée pour trier votre
collection en cas de besoin. Mettez la à false si vous voulez garder la
collection comme telle.
L’adapter vous permet aussi de créer l’état initial à travers la méthode
getInitialState. Elle peut prendre en paramètre un objet avec les
valeurs initiaux des paramètres autres que entities et id.

[Link] 712
NgRx
NgRx Entity
Entity Adapter
 L’adapters fournit plusieurs méthodes :
 addOne: ajoute une entité dans la collection.
 addMany: ajoute plusieurs entités dans la collection.
 setAll: Remplace la collection courante par la collection fournie.
 setOne: Ajoute ou remplace une entité dans la collection.
 setMany: Ajoute ou remplace plusieurs entités dans la collection.
 removeOne: Supprime une entité de la collection.
 removeMany: Supprime plusieurs entités de la collection.
 removeAll: Vide la collection.

[Link] 713
NgRx
NgRx Entity
Entity Adapter
 L’adapters fournit plusieurs méthodes :
 updateOne: Met à jour une entité de la collection, il supporte la
mise à jour partielle.
 updateMany: Met à jour plusieurs entités de la collection, il
supporte la mise à jour partielle.
 upsertOne: ajoute ou met à jour une entité de la collection.
 upsertMany: ajoute ou met à jour plusieurs entités de la collection.
 mapOne: Met à jour une entité dans la collection en définissant une
fonction de mapping.
 map: Met à jour plusieurs entités dans la collection en définissant
une fonction de mapping..
[Link] 714
NgRx
NgRx Entity
Entity Adapter
 Afin de sélectionner vos entities et de les transformer pour utilisation,
l’adapter offre une panoplie de selector prêt à l’emploi.
 Pour récupérer la liste des sélecteurs, utiliser la méthode getSelectors
La méthode getSelectors renvoie des sélecteurs NgRx qui fournissent des
fonctions pour sélectionner des informations dans la collection d'entités.
 selectIds : sélectionne un tableau d'identifiants
 selectEntities : sélectionne le dictionnaire d'entités. Nous pouvons l'utiliser
pour récupérer l'entité par identifiant.
 selectAll : sélectionne un tableau de toutes les entités sous format.
 selectTotal : sélectionne le nombre total d'entités.

[Link] 715
NgRx
NgRx Entity
Entity Adapter

export const {
selectAll,
selectEntities,
selectIds,
selectTotal } =
[Link]();
716
NgRx
NgRx Data
 Le problème majeur avec la gestion des entités et NgRx Entity est la
répétition de code.
 Pour chaque opération, nous devons créer les actions, reducers, effects
et selectors.
 Afin de faciliter cette tache, NgRx propose l’extension NgRx Data.
 NgRx Data est une abstraction sur le store, les effects et les entity qui
réduit radicalement la quantité de code que vous écrivez.
 Il offre des implémentations par défaut ( se base sur les
conventions) tout en fournissant de nombreux points d'extension pour
modifier ou augmenter ces comportements par défaut.

[Link] 717
NgRx
NgRx Data
 Comme pour toute abstraction, alors que vous gagnez en simplicité,
vous perdez le caractère explicite de l'interaction directe avec NgRx.
 NgRx Data n’est pas adapté aux données « non-entités ».
 Les types scalaires, les énumérations, les données de session et autres
sont mieux gérées avec le NgRx standard.
 Vous pouvez donc combiner selon le type des données à traiter les
différentes techniques NgRx.

[Link] 718
NgRx
NgRx Data
Installation
 Afin d’installer NgRx Data utilisez la commande suivante :
ng add @ngrx/data@latest
 Il est nécessaire d’avoir déjà installé NgRx Store, NgRx Effects et
NgRx Entity.
 Vous devez aussi appelez les modules Store et Effects avant le
DataModule.

[Link] 719
NgRx
NgRx Data
Entity
 NgRx Data gère des
collections d’Entités. Il
maintient un cache ou il
stocke ces informations.
 Une collection d’entité
implémente l’interface
EntityCollection

[Link] 720
NgRx
NgRx Data
Entity Metadata
 NgRx Data conserve un cache des collections d'entités dans le
Store.
 Donc et afin de spécifier à NgRx Data les entités qu’elle doit gérer,
vous devez passer par l’objet EntityMetadataMap.
 Chaque élément de cet objet est un objet de type EntityMetadata.
 La clé représente le nom de l’identifiant de l’objet et le nom par
défaut de l’entité.
 Afin d’associer cet objet à votre NgRxDataModule, ajoutez le dans
la config sous la clé entityMetadata

[Link] 721
NgRx
NgRx Data
Entity Metadata
const entityMetadata: EntityMetadataMap = {
Cv: {}
};
export const entityConfig: EntityDataModuleConfig = {
entityMetadata,
};

[Link](entityConfig),
[Link]

[Link] 722
NgRx
NgRx Data
Entity Metadata
 Afin de configurer NgRx Data pour export function ageAndNameFilter(
cvs: Cv[], pattern: { filter: string; ageMin: number }) {
savoir comment gérer votre entité, return [Link](
vous avez plusieurs propriétés. (cv) => [Link]([Link]) &&
[Link] > [Link]
 entityName : Si le nom de l’entité );
est différent de la clé, vous pouvez le }

modifier avec entityName. const entityMetadata: EntityMetadataMap = {


Cv: {
 filterFn : Vous permet de spécifier entityName: "Cv",
une fonction de filtrage de vos entités. filterFn: ageAndNameFilter,
selectId: (cv: Cv) => [Link],
 selectId : L’identifiant de votre },
};
entité (par défaut id)
[Link] 723
NgRx
NgRx Data
Entity Metadata
 sortComparer : Par défaut les export function compareCv(cv1: Cv, cv2: Cv) {
return [Link] - [Link];
éléments sont affichés dans l’ordre }
dans lequel ils sont récupérés. Afin de
spécifier l’ordre que vous souhaitez, const entityMetadata: EntityMetadataMap = {
passez la fonction de comparaison Cv: {
entityName: "Cv",
(fonctionnement de NgRx Entity) filterFn: ageAndNameFilter,
selectId: (cv: Cv) => [Link],
 additionalCollectionState: Permet sortComparer: compareCv,
d’ajouter des propriétés spécifiques. },
};
 Il faut ensuite faire le nécessaire pour
dire à NgRx Data comment les gérer

[Link] 724
NgRx
NgRx Data
Entity Metadata
 entityDispatcherOptions : permet de définir le comportement de
NgRxData vis-à-vis des opérations de CRUD (optimistic or
pesimitic)
 Les valeurs par défaut sont les plus sûres : optimiste pour la
suppression et pessimiste pour l'ajout et la mise à jour.
 Prend en paramètre un objet:
optimisticAdd: boolean;
optimisticDelete: boolean;
optimisticUpdate: boolean;
optimisticUpsert: boolean;
optimisticSaveEntities: boolean;
[Link] 725
NgRx
NgRx Data
EntityCollectionService
 Le service EntityCollectionService est une façade sur le dispatcher
et le sélecteur qui gèrent les collections d’entités.
 Le dispatcher offre les commandes qui dispatches les entity Actions.
 Ces commandes mettent à jour directement la collection d'entités
ou déclenchent des requêtes HTTP vers un serveur.
 Lorsque le serveur répond, NgRx distribue de nouvelles actions
avec les données de réponse et ces actions mettent à jour la
collection d'entités.
 Les Selectors sont des propriétés renvoyant des observables de
sélecteur. Chaque observable surveille un changement spécifique
dans la collection d'entités en cache et émet la valeur modifiée.
[Link] 726
NgRx
NgRx Data
Entity Commands

[Link] 727
NgRx
NgRx Data
EntityCollectionService
Property Description
entityName: string [Link] of the entity collection for these selectors$
collection$: Observable<EntityCollection>
[Link] of the collection as a whole
| Store<EntityCollection>
count$: Observable<number> [Link] of count of entities in the cached
| Store<number> collection.
entities$: Observable<T[]> | Store<T[]> [Link] of all entities in the cached collection.
entityActions$: Observable<EntityAction> [Link] of actions related to this entity type.
entityMap$: Observable<Dictionary<T>>
[Link] of the map of entity keys to entities
| Store<Dictionary<T>>
[Link] of error actions related to this entity
errors$: Observable<EntityAction>
type.

[Link] 728
NgRx
NgRx Data
EntityCollectionService
Property Description
[Link] of the filter pattern applied by the
filter$: Observable<string> | Store<string>
entity collection's filter function
[Link] of entities in the cached collection
filteredEntities$: Observable<T[]> | Store<T[]>
that pass the filter function
keys$: Observable<string[] | number[]> | Store<string[] [Link] of the keys of the cached collection,
| number[]> in the collection's native sort order
[Link] true when the collection has been
loaded$: Observable<boolean> | Store<boolean>
loaded
[Link] true when a multi-entity query
loading$: Observable<boolean> | Store<boolean>
command is in progress.
changeState$: Observable<ChangeStateMap<T>> [Link] (including original values) of
| Store<ChangeStateMap<T>> entities with unsaved changes
[Link] 729
NgRx
NgRx Data
EntityCollectionService
 Afin d’utiliser le EntityCollectionService, vous avez deux options :
1. Injecter le EntityCollectionServiceFactory et utiliser sa méthode
create qui vous créera un service de la classe EntityCollectionService.
2. Créer un service qui étend EntityCollectionServiceBase

export class CvComponent {


dataCvService: EntityCollectionService<Cv>;
constructor(
1
private entityCollectionServiceFactory: EntityCollectionServiceFactory
){
[Link] = [Link]<Cv>("Cv");
}
[Link] 730
NgRx
NgRx Data
EntityCollectionService
 Créer un service qui étend EntityCollectionServiceBase
 Injecter l’EntityCollectionServiceElementsFactory
 Appeler le constructeur de EntityCollectionServiceBase et lui
passer le nom de la collection et
l’EntityCollectionServiceElementsFactory
export class CvService extends EntityCollectionServiceBase<Cv> {
constructor(
private http: HttpClient,
serviceElementsFactory: EntityCollectionServiceElementsFactory 2
){
super("Cv", serviceElementsFactory);
}
731
NgRx
NgRx Data
EntityCollectionService
 Lorsque vous utilisez l’une des fonctionnalités qui accèdes à une Api
externe, NgRx Data suit une convention qui est d’utiliser
automatiquement votre nom de domaine auquel elle concatène ‘/api’
puis ‘/collectionName’
 Pour la collection ‘Cv’, si vous chercher la liste des cvs vous aurez
l’appel suivant : localhost:4200/api/cvs
 un ‘s’ est ajouté pour les collections.
 Si le pluriel du nom de la collection ne correspond pas, spécifier le
dans la propriété pluralNames de l’EntityDataModuleConfig
export const entityConfig: EntityDataModuleConfig = {,
pluralNames : { 'hero': 'heroes'},
};
[Link] 732
NgRx
NgRx Data
EntityCollectionService
 Si l’url ne correspond pas, vous pouvez la spécifiez en définition un
objet de type DefaultDataServiceConfig et en le providant.
 La propriété root permet de spécifier l’uri à concaténer au nom des
collections.
 La propriété entityHttpResourceUrls permet de spécifier l’uri poru
chaque collection. Si la collection n’est pas spécifié, il utilisera celle
de la root.

[Link] 733
NgRx
NgRx Data
EntityCollectionService
const defaultDataServiceConfig: DefaultDataServiceConfig = {
root: "[Link]
entityHttpResourceUrls: {
Cv: {
entityResourceUrl: "[Link]
collectionResourceUrl: "[Link]
},
},
};

[Link] 734
export declare abstract class DefaultDataServiceConfig {
/**
* root path of the web api. may also include protocol, domain, and port
* for remote api, e.g.: `'[Link] (default: 'api')
*/
root?: string;
/**
* Known entity HttpResourceUrls.
* HttpUrlGenerator will create these URLs for entity types not listed here.
*/
entityHttpResourceUrls?: EntityHttpResourceUrls;
/** Is a DELETE 404 really OK? (default: true) */
delete404OK?: boolean;
/** Simulate GET latency in a demo (default: 0) */
getDelay?: number;
/** Simulate save method (PUT/POST/DELETE) latency in a demo (default: 0) */
saveDelay?: number;
/** request timeout in MS (default: 0)*/
timeout?: number;
/** to keep leading & trailing slashes or not; false by default */
trailingSlashEndpoints?: boolean;
} [Link] 735
Angular
Débogage et Tests
Unitaires
AYMEN SELLAOUTI

736
Références

737
Plan du Cours
1. Introduction
2. Les composants
3. Les directives
4. Service et injection de dépendances
5. Le routage
6. Forms
7. HTTP Module
8. Tests unitaires
738
Débogage angular 9 et Ivy
 A partir de angular 9 et l’apparition du compilateur ivy, la methode [Link] ne fonctionne plus et
nouvel api a été proposé.

 ng possède une multitude de méthodes.

 getComponent au quel vous passez l’objet composant ($0,$1,…) vous permet de récupérer l’instance
du composant.

 [Link](el) returne le composant parant du composnat selectionné.

[Link](el) returne les directives associées à l’élément;

739
Angular DevTools (Compatible avec
Angular 9 et +)
 Angular Devtools est l’alternative offerte par Angular à Augury pour
la version 9 et plus d’Angular)
 C’est une extension que vous pouvez trouver dans Chrome ou
Firefox.
[Link]
devtools/ienfalfjdbdpebioblfackkekamfmbnh
 Une fois installée, vous aurez une onglet dans votre outils de
développeur qui s’appelle angular.

[Link] 740
Angular DevTools (Compatible avec
Angular 9 et +)
 Lorsque vous y accéder vous
avez deux options :
Components : qui vous permet
d’explorer les composants et les
directives et de voir ou éditer leur
état.
Profiler - vous permet de profiler
votre application et voir ce qui se
passe dedans.

[Link] 741
Angular DevTools (Compatible avec
Angular 9 et +)
 Lorsque vous y accéder vous
avez deux options :
Components : qui vous permet
d’explorer les composants et les
directives et de voir ou éditer leur
état.
Profiler - vous permet de profiler
votre application et voir ce qui se
passe dedans.

[Link] 742
Angular
Tests Unitaires
et E2E
AYMEN SELLAOUTI

743
Références

744
Tests unitaires : Introduction
 La plus petite unité de test possible

Couvre une petite fonctionnalité et ne s’occupe pas de comment les


différents unités testées travaillent ensemble.

Il est isolé et ne doit pas dépendre d’autres tests.

 Rapide, fiable et pointe directement sur le bug en question.

[Link] 745
Tests unitaires : Pourquoi
 Rend votre code plus robuste, le bug sera identifié plus tôt
 Vous permet de formaliser et de documenter vos besoins
 Un bon test décrit clairement comment le code d'implémentation
doit se comporter
 Un bon test doit couvrir les scénarios les plus importants
 Les tests rendent le changement sûr en empêchant les régressions

746
Tests unitaires : Partout ?
 Alors devriez-vous écrire des tests automatisés pour tous les cas
possibles afin de garantir l'exactitude ?
 Non, disent les principes de l'ISTQB : "Les tests exhaustifs sont
impossibles".
 Il n'est ni techniquement faisable ni utile d'écrire des tests pour
toutes les entrées et conditions possibles.
 Au lieu de cela, vous devez évaluer les risques d'un certain cas et
rédiger d'abord des tests pour les cas à haut risque.
 Même s'il était viable de couvrir tous les cas, cela vous donnerait un
faux sentiment de sécurité.
747
Angular et les tests unitaires
 Angular avec sa structuration et ses différentes couches se marie
parfaitement avec les tests unitaires.
 Chaque couche ayant un rôle unique et une tache bien spécifique,
les tests unitaires seront donc propres à chaque partie,
composant, pipe, service, directive, …
 L’utilisation de l’injection de dépendance implique le couplage
faible et donc des tests isolés.
 Les providers qui permettent de fournir des classes fictives
facilitent aussi cette notion d’isolation.

748
Jasmin et Karma
 En installant Angular via le Cli, vous trouverez prêt à l’emploi deux
outils de tests : Jasmin et Karma.

"@types/jasmine": "~4.3.0",
"jasmine-core": "~4.5.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.0.0",

749
Jasmin et Karma

Jasmin est un framework permettant de faciliter la création de tests. Il contient un


ensemble de fonctionnalités permettant d’écrire plusieurs types de test.

karma est un task runner pour vos tests. Il utilise un fichier de


configuration afin de gérer le process de test en identifiant les
fichiers de chargement, le framework de test, le navigateur à
lancer…
750
Lancement d’un test
 Afin de lancer un test, vous avez juste besoin d’une seule commande et
Karma fait le reste. Il exécutera les tests, ouvrira le navigateur, et
affichera un rapport sur l’ensemble des tests.
ng test

751
Concepts de base de jasmin
describe (suite)
 Pour ce qui est de Jasmine, un test se compose d'une ou plusieurs
suites. Une suite est déclarée avec un bloc describe :

describe('Suite description', () => {


/* … */
});

 Chaque suite décrit un morceau de code, le code à tester.

[Link] 752
Concepts de base de jasmin
describe (suite)
 describe est une fonction qui prend deux paramètres.
 Une chaîne. généralement le nom de la fonction ou de la classe testée. Par
exemple, describe(‘TestExampleComponent‘)
 Une fonction contenant la définition de la suite
 On peut avoir des describe imbriqué afin de diviser des gros describe
en des sections logiques describe('Suite description', () => {
describe('One aspect', () => {
Les blocs de description imbriqués ajoutent /* … */
une description lisible à un groupe de });
describe('Another aspect', () => {
spécifications. Ils peuvent également héberger /* … */
leur propre logique de configuration. });
});

753
Concepts de base de jasmin
Specification (it)
 Chaque describe se compose d'une ou plusieurs spécifications.
 Une spécification est déclarée avec un bloc it :
describe('description de la suite', () => {
it('description de la spécification', () => {
/* … */
});
});

 it est une fonction qui prend deux paramètres.


Le premier paramètre est une chaîne avec une description lisible
Le second paramètre est une fonction contenant le code de votre test

754
Concepts de base de jasmin
Specification (it)
 Le pronom it fait référence au code testé.
 La phrase doit donc être lisible et affirme le comportement du code
testé.
 Le code de votre spécification prouve alors cette affirmation.
 Ce style d'écriture des spécifications provient du concept de Behavior-
Driven Development (BDD).
 L'un des objectifs du BDD est de décrire le comportement du logiciel
dans un langage naturel.
 Chaque partie prenante doit être capable de lire les phrases et de
comprendre comment le code est censé se comporter.

755
Concepts de base de jasmin
Specification (it)
 Pour écrire le titre de votre test, le it…, demandez vous ce que doit
faire le code que vous testez.
 Pour une LampeComponent par exemple, il doit allumer et éteindre la
lampe, on aura donc :
it('switch on the lamp', () => {
/* … */
});
it('switch off the lamp', () => {
/* … */
})

 Après it, un verbe suit généralement, comme switch on

756
Concepts de base de jasmin
Specification (it)
 À l’intérieur du bloc it se trouve le code de test réel.
 Indépendamment du framework de test, le code de test se compose généralement
de trois phases :
Arrange
Act
Assert.
 Arrange est la phase de préparation et de mise en place. Par exemple, la classe
testée est instanciée. Les dépendances sont mises en place. Des espions (spy) et des
faux sont créés.
 Act est la phase où l'interaction avec le code testé. Par exemple, une méthode est
appelée ou un élément HTML du DOM est cliqué.
 Assert est la phase où le comportement du code est contrôlé et vérifié. Par
exemple, la sortie réelle est comparée à la sortie attendue.

757
Concepts de base de jasmin
Specification (it)
Imaginons que nous voulons tester un service qui permet
d’additionner et de soustraire des entiers.
Nous commençons par tester l’addition.
Arrange
 Nous devons créer une instance du service et de ses dépendances
s’ils existent
Act
 Appeler la fonction add avec deux paramètres
Assert.
 Vérifier que la fonction retourne le bon résultat

758
Concepts de base de jasmin
Attente (Expectation)
Dans la phase d'affirmation (Assert), le test compare la sortie ou la
valeur de retour réelle à la sortie ou à la valeur de retour attendue.
S'ils sont identiques, le test réussit. S'ils diffèrent, le test échoue.
 Afin de gérer ca, jasmine nous offre la fonction expect.
 Cette fonction est associée à un ensemble de matchers permettant
de faciliter la validation de vos attentes ou expectations.

const expectedValue = 5;
const actualValue = [Link](2, 3);
expect(actualValue).toBe(expectedValue);

759
Jasmin matchers
Les matchers de Jasmine sont des fonctions qui permettent de tester si une
valeur donnée correspond à une condition spécifique. Ils permettent donc de
vérifier que les fonctionnalités de l'application se comportent comme prévu.
toBe() : vérifie si deux valeurs sont strictement égales (utilisant l'opérateur "===")
toEqual() : vérifie si deux objets ont les mêmes propriétés et les mêmes valeurs
toMatch() : vérifie si une chaîne de caractères correspond à une expression
régulière
toBeDefined() : vérifie si une variable est définie
toBeUndefined() : vérifie si une variable n'est pas définie
toBeNull() : vérifie si une variable est null
[Link] 760
Jasmin matchers
toBeTruthy() : vérifie si une expression est vraie
toBeFalsy() : vérifie si une expression est fausse
toContain() : vérifie si un tableau ou une chaîne de caractères contient
un élément spécifié
toBeLessThan() : vérifie si une valeur est inférieure à une autre
toBeGreaterThan() : vérifie si une valeur est supérieure à une autre
…

[Link] 761
Concepts de base de jasmin
 describe (string, function) : fonction qui prend en paramètre un
titre et une ensemble de test individuel.
 it (string, function) : fonction représentant un test individuel qui
prend en paramètre un titre et une fonction définissant un test
individuel.
 expect : fonction qui retourne un booléen et évalue une expectation
un besoin à valider par le test unitaire.
Exemple expect(etatActuel).toBe(etatExpecté)
 les matchers : sont des helpers prédéfinis permettant différentes
validations.
762
Concepts de base de jasmin
 xit permet d’exclure un test individuel

 xdescribe permet d’exclure tout le bloc

 fit permet de spécifier le test individuel à exécuter

 fdescribe permet de spécifier le bloc à exécuter.

763
Exercice
 Créer un service MathService
 Ajouter y deux fonction add et substract
 Créer les tests nécessaires pour ce service

764
Concepts de base de jasmin
 Lorsque vous écrivez plusieurs spécifications dans une suite, vous
réalisez rapidement que la phase d'arrangement (Arrange) est
similaire, voire identique, dans toutes ces spécifications.
 Par exemple, lors du test du MathService, la phase Arrange consiste
toujours à créer une instance de MathService.
 Afin de centraliser ces traitements réplétifs, Jasmine propose quatre
fonctions : beforeEach, afterEach, beforeAll et afterAll. Ils sont
appelés à l'intérieur d'un bloc describe.
 Ils attendent un paramètre, une fonction qui est appelée aux étapes
données.
765
Concepts de base de jasmin
Jasmin offre des handlers permettant de répéter certaines fonctionnalités.
beforeEach : prend en paramètre une callback et la répète avant
chaque spec it.
afterEach : prend en paramètre une callback et la répète après
chaque spec it.
beforeAll : prend en paramètre une callback et la répète avant
chaque suite describe.
afterEach : prend en paramètre une callback et la répète après
chaque suite describe.
766
Exercice
 Modifier vos tests en utilisant ces nouvelles fonctionnalités
 Ajouter un service loggerService permettant de loger ce que vous
voulez.
 Injecter le dans le MathService
 Faite en sorte que vous logez chaque opération opérée
(Exemple l’appel de add(2,3) devra loger 3 + 2 = 5)
 Mettez à jour vos tests

767
Simuler les dépendances
 Dans plusieurs cas, et pour garder l’isolation de vos tests, vous devez
simuler les dépendances de l’élément à tester.
 Ce remplacement s’appelle ‘Mocking’ et le remplaçant un ‘mock’
 Un mock doit avoir la même forme que l'original. Si la dépendance
est une fonction, le mock doit avoir la même signature, c'est-à-dire les
mêmes paramètres et la même valeur de retour.
Le mock n'a pas besoin d'être complet, mais suffisant pour servir
de remplacement. Il doit être équivalent à l'original en ce qui
concerne le code testé, pas entièrement égal à l'original.

768
Jasmin spies
 Les jasmine spies sont des outils utilisés pour tester les fonctions dans le
cadre des tests unitaires dans le framework Jasmine pour JavaScript.
 Ils permettent de surveiller l'interaction d'une fonction avec ses
dépendances, en remplaçant ces dépendances par des objets de surveillance
(spies) qui enregistrent les appels et les arguments passés à ces dépendances.
 Afin de créer un spy vous pouvez utiliser la méthode createSpyObj de
Jasmine qui permet de créer un objet "espion" (spy) pour les tests unitaires
dans Angular.
Cet objet est utilisé pour simuler les dépendances d'un composant ou
d'un service, et permet de vérifier que ces dépendances sont utilisées
correctement. Il peut également être utilisé pour définir des
comportements spécifiques pour ces dépendances lors des tests.
769
Jasmin spies
 createSpyObj prend en premier paramètre une chaine identifiant le
spy et en second paramètre un tableau avec l’ensemble des
méthodes à simuler dans votre objet ou un objet avec comme clé
l’élément et comme valeur sa valeur.
oneSpy = [Link]('ExampleService', ['fn1', 'fn2']);
const service = new Service(oneSpy);
oneSpy = [Link]<ExempleService>(
'ExampleService', {
'f1': undefined,
'f2': () => {},
});
 Ici, oneSpy simule un objet ayant une fonction fn1 et une fonction fn2.
770
Jasmin spies
 spyOn(object, methodName) : Cette fonction permet de créer un
espion pour une méthode donnée d'un objet. Par exemple, pour
espionner la méthode getData d'un service myService, vous pouvez
utiliser :
let spy: [Link] = spyOn(myService, 'getData');
[Link]() : Cette fonction permet de configurer l'espion
pour qu'il appelle la méthode originale sous-jacente lorsqu'il est appelé.
Par exemple, pour continuer à appeler la méthode getData lorsque
l'espion est appelé, vous pouvez utiliser :
[Link]();
771
772
Jasmin spies
 [Link](value) : Cette fonction permet de configurer
l'espion pour qu'il renvoie une valeur spécifique lorsqu'il est appelé. Par
exemple, pour faire en sorte que l'espion de la méthode getData renvoie
une valeur de test prédéfinie, vous pouvez utiliser :
[Link](testData);
[Link](fn) : Cette fonction permet de configurer l'espion pour
qu'il appelle une fonction de rappel spécifiée lorsqu'il est appelé.
Par exemple, pour faire en sorte que l'espion de la méthode getData appelle
une fonction de rappel qui génère des données de test, vous pouvez utiliser :
[Link](() => generateTestData());
773
Jasmin spies
 [Link]() : Cette propriété permet de récupérer le nombre de
fois que l'espion a été appelé. Par exemple, pour vérifier que l'espion de
la méthode getData a été appelé trois fois, vous pouvez utiliser :
expect([Link]()).toEqual(3);

[Link](index) : Cette propriété permet de récupérer les


arguments passés à l'espion lors de l'appel spécifié. Par exemple, pour
vérifier que l'espion de la méthode getData a été appelé avec l'argument
id égal à 5 lors de son deuxième appel, vous pouvez utiliser :
expect([Link](1)).toEqual([5]);
774
Jasmin spies
[Link](error): Cette fonction permet de configurer l'espion pour
qu'il lance une erreur spécifiée lorsqu'il est appelé. Cela peut être utile pour
tester les gestionnaires d'erreurs de votre code.
[Link]() : Cette propriété permet de récupérer tous les appels enregistrés
pour l'espion. Cela peut être utilisé pour vérifier les arguments passés à l'espion
pour chaque appel.
[Link]() : Cette propriété permet de récupérer les informations
de l'appel le plus récent à l'espion. Cela peut être utilisé pour vérifier les
arguments passés à l'espion pour l'appel le plus récent.
[Link]() : Cette propriété permet de récupérer les informations de l'appel
le plus ancien à l'espion. Cela peut être utilisé pour vérifier les arguments passés à
l'espion pour l'appel le plus ancien.
[Link]() : Cette fonction permet de configurer l'espion pour qu'il ne fasse
rien lorsqu'il est appelé. Cela peut être utilisé pour tester des scénarios où une
dépendance ne doit pas être appelée.
775
Jasmin Matchers
 toHaveBeenCalled : permet de vérifier si une fonction simulée a été
appelée au moins une fois.
 toHaveBeenCalledTimes : permet de vérifier qu'une fonction simulée
a été appelée un nombre spécifique de fois.
toHaveBeenCalledWith : permet de vérifier si une fonction simulée a
été appelée avec des arguments spécifiques.
toHaveBeenLastCalledWith : permet de vérifier si une fonction
simulée a été appelée avec des arguments spécifiques lors de son dernier
appel.
toHaveBeenNthCalledWith : permet de vérifier si une fonction simulée
a été appelée avec des arguments spécifiques lors de son n-ième appel.
776
Jasmin Matchers
toHaveReturned : permet de vérifier si une fonction simulée a
retourné une valeur spécifique.
toHaveReturnedTimes : permet de vérifier si une fonction simulée a
retourné une valeur spécifique un nombre de fois spécifié.
toHaveReturnedWith : permet de vérifier si une fonction simulée a
retourné une valeur spécifique lors de son dernier appel.
toHaveLastReturnedWith : permet de vérifier si une fonction
simulée a retourné une valeur spécifique lors de son dernier appel.
toHaveNthReturnedWith : permet de vérifier si une fonction
simulée a retourné une valeur spécifique lors de son n-ième appel.
777
Exercice
 Utilisez les spy pour gérer le loggerService
 Tester le fait que la fonction a été appelé le bon nombre de fois et
qu’elle a reçu les bon paramètres.

778
Exercice
 Proposer un test à ce service.
 Vous devez vous assurez que le service existe, et qu’il fasse le
nécessaire import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class AuthService {
authenticated = false;
constructor() {}
isAuthentified(): boolean {
[Link] = !![Link]('user');
return [Link];
}
}
779
Tester un pipe
 Un pipe contient uniquement la méthode transform. Tester un pipe
revient donc à tester la méthode transform.

 Reprenez le pipe defaultImage et testez le

780
Angular TestBed
 TestBed est l’outil le plus important des utilitaires de test Angular.
 Il vous facilite l’étape de préparation de l’environnement de Test
(Arrange).
 Il permet de construire dynamiquement un module de test afin de
simuler un Module Angular.
En utilisant la méthode configureTestingModule de TestBed vous avez
accès à une grande partie des propriétés de @NgModule.
 La méthode configureTestingModule prend en paramètre l’objet
permettant de définir un module.
Ceci vous permet aussi d’accéder au système d’injection de dépendance
d’Angular.
781
Angular TestBed
beforeEach(async () => {
await [Link]({
imports: [
LesModulesNecessaires
],
declarations: [
AppComponent
],
});
});
782
Angular TestBed
 Une fois le module configuré, nous devons utiliser la méthode
compileComponents de TestBed, qui va compiler toutes les
déclarations (composants, pipes et directives).
 Cette fonction retourne une promesse.
 Etant donné que la méthode configureTestingModule retourne un
TestBed, nous pouvons chainer l’appel avec la déclaration du
module.

783
Angular TestBed
beforeEach(async () => {
await [Link]({
imports: [
RouterTestingModule
],
declarations: [
AppComponent
],
}).compileComponents();
});
784
Récupérer un service injecté avec TestBed
 Afin de récupérer un service injecté, TestBed offre la méthode inject
qui prend en paramètre la classe du service injecté et retourne une
instance.

Exemple [Link](MonService);

785
Tester un service HTTP
 Dans un test unitaire, on ne veut pas vraiment appeler le serveur
HTTP : ce n’est pas ce que nous testons. Nous voulons faire un
"faux" appel HTTP pour retourner de fausses données.
 Pour cela, nous pouvons remplacer la dépendance au
service HttpClient du HttpClientModule par une implémentation
mocké en important le HttpClientTestingModule du
'@angular/common/http/testing'
 Le service HttpTestingController du HttpClientTestingModule
sera alors utilisée pour tester les services qui utilisent l'objet HttpClient.
Il permet de simuler des requêtes HTTP et de vérifier les
réponses, en interceptant la requête http envoyé.
786
Tester un service HTTP
Les méthodes du HttpTestingController
expectOne(url: string | RegExp) : Cette méthode s'attend à ce qu'il y
ait une seule requête correspondant à l'URL ou à l'expression régulière
spécifiée. Elle renvoie un objet HttpRequest qui permet de vérifier les
propriétés de la requête.
verify() : Cette méthode vérifie qu'il n'y a plus de requêtes en attente.
Si il y en a, une erreur est levée. Généralement utilisé dans le
BeforeEach.

787
Tester un service HTTP
Les méthodes du TestRequest
La méthode expectOne vous retourne un
objet de type TestRequest.
 Elle vous permet à travers sa propriété request
de récupérer la requête en cours.
 Elle offre aussi la méthode flush(data: any,
options?: {headers?: HttpHeaders, status?: number,
statusText?: string}) : Cette méthode envoie une
réponse à toutes les requêtes en attente. Les
options peuvent être utilisées pour spécifier les en-
têtes, le statut et le statut texte de la réponse.
 Pour retourner une erreur elle offre la méthode
error, qui prend en paramètre un objet
ProgressEvent et un objet d’option permettant
d’ajouter le status et le statusText par exemple.
788
Tester un service HTTP
Généralement, les tests vont suivre le processus suivant :
1. Appelez la méthode testée qui envoie des requêtes HTTP.
2. Récupérer les requêtes en attente
3. Répondre à ces requêtes avec de fausses données.
4. Vérifier le résultat de l'appel de la méthode
5. Vérifier que toutes les requêtes ont été gérées

789
it('return all cvs', () => {
// ACT
[Link]().subscribe((cvs) => {
fdescribe('CvService', () => { [Link]('cvs', cvs);
let service: CvService;
let http: HttpTestingController; // Assert
beforeEach(() => { /* J'attend que la liste des cvs existe */
// Arrange expect(cvs).toBeTruthy();
[Link]({ });
imports: [HttpClientTestingModule],
}); //Act
service = [Link](CvService); const req = [Link]([Link]);
http = [Link](HttpTestingController);
}); expect([Link]).toBe('GET');

it('should be created', () => { [Link](CVS);


expect(service).toBeTruthy(); });
}); });

790
Tester un Http Service
 Finaliser la méthode getCvs
 Tester GetById
 Ajouter un test de mise à jour qui a échoué du à un id d’un cv qui
n’existe pas

791
Tester un composant
 Les composants sont le corps d’Angular
 Lors de la conception d'un test de composant, les questions directrices
sont :
 que fait le composant
 que doit-on tester ?
 comment tester ce comportement ?
 qu’est ce qui est nécessaire pour tester le composant ?

792
Angular TestBed
 TestBed vous facilite l’étape de préparation de l’environnement de
Test (Arrange).
 Il permet de construire dynamiquement un module de test et nous
permet donc de répondre à la question qu’est ce qui est nécessaire pour
tester le composant ?
 Avec configureTestingModule récupérer les dépendances de votre
composant, à savoir les diretives, pipes et autres composant qu’il
utilise via la propriété declarations.
 Les modules nécessaires à son fonctionnement via imports.
 Les services dont il a besoin via providers.

793
Component fixtures
 Le composant étant formé d’une partie métier (L’instance du
composant) et d’une partie vue (son template), nous devons avoir le moyen
de les récupérer.
 La méthode createComponent de TestBed prend en paramètre un
composant et en crée un objet de type ComponentFixture contenant ses
deux partie.
Exemple :
fixture = [Link](AppComponent);
Cette classe permet d’encapsuler un composant et son template et offre
une multitude d’attributs et de méthodes très utiles pour tester un
composant.

[Link] 794
Component fixtures
changeDetection
 createComponent affiche le composant dans un élément conteneur div dans le
DOM HTML.
 Cependant, il manque quelque chose. Le composant n'est pas entièrement
affiché.
 Tout le HTML statique est présent, mais le HTML dynamique est absent.
Toute la partie binding nécessitant un changeDetection pour s’afficher ne sera
pas la.
 En effet, dans l’environnement de test, il n'y a pas de détection automatique
des changements.
 En testant le code, nous devons déclencher la détection de changement
manuellement.
Pour ce faire, utiliser la méthode detectChanges du fixture
[Link]();

795
Component fixtures
debugElement
 debugElement est un objet fourni par @angular/core/testing qui
permet d'accéder aux éléments du DOM lors des tests unitaires dans
Angular. Il est utilisé en conjonction avec ComponentFixture, qui permet
de créer une instance d'un composant pour les tests.
Voici quelques méthodes courantes que vous pouvez utiliser pour
sélectionner des éléments du DOM à l'aide de debugElement :
query(predicate: Predicate<DebugElement>) : DebugElement : permet
de sélectionner le premier élément qui correspond au prédicat spécifié.
queryAll(predicate: Predicate<DebugElement>) : DebugElement[] :
permet de sélectionner tous les éléments qui correspondent au prédicat
spécifié.
796
debugElement
nativeElement : permet d'accéder à l'élément natif du DOM
correspondant à l'élément sélectionné. Vous pouvez utiliser cette
propriété pour accéder aux propriétés standard de l'élément, telles que
innerHTML, className, etc.
properties : permet d'accéder aux propriétés de liaison de l'élément
sélectionné. Vous pouvez utiliser cette propriété pour accéder aux
propriétés liées à l'élément, telles que [ngClass], [value], etc.
attributes : permet d'accéder aux attributs de l'élément sélectionné.
Vous pouvez utiliser cette propriété pour accéder aux attributs de
l'élément, tels que id, name, etc.

797
debugElement
classes : permet d'accéder aux classes CSS de l'élément sélectionné.
Vous pouvez utiliser cette propriété pour vérifier si l'élément a une
classe spécifique ou pour obtenir la liste de toutes les classes de
l'élément.
styles : permet d'accéder aux styles de l'élément sélectionné. Vous
pouvez utiliser cette propriété pour vérifier si l'élément a un style
spécifique ou pour obtenir la liste de tous les styles de l'élément.
childNodes : permet d'accéder aux enfants de l'élément sélectionné.
Vous pouvez utiliser cette propriété pour accéder aux enfants de
l'élément, soit pour les sélectionner soit pour accéder à leur propriétés.

798
debugElement
Les propriétés parent,
children et childNodes
facilitent la navigation dans
l'arborescence DOM. Ils
renvoient également un
DebugElements.

799
debugElement
nativeElement
 Il est souvent nécessaire de récupérer le DebugElement pour accéder à l'élément
DOM natif.
 Chaque DebugElement a une propriété nativeElement.
 nativeElement est typé comme any car Angular ne connaît pas le type exact de
l'élément DOM.
 La plupart du temps, il s'agit d'une sous-classe de HTMLElement. Lorsque
vous utilisez nativeElement, vous devez en savoir plus sur l'interface DOM de
l'élément spécifique.
 Par exemple, un élément de bouton est représenté sous la forme
HTMLButtonElement dans le DOM.

800
debugElement
Requêter le DOM
 Chaque DebugElement comporte les méthodes query et queryAll
pour rechercher des éléments descendants. Ils prennent en paramètre un
prédicat.
 Il existe plusieurs prédicats prédéfinis que vous pouvez utiliser
pour sélectionner des éléments du DOM :
[Link](selector: string) : permet de sélectionner des éléments en
fonction d'un sélecteur CSS.
[Link](type: Type<any>) : permet de sélectionner des éléments
qui ont une directive spécifique.
[Link] : permet de sélectionner tous les éléments.
801
import { ComponentFixture, TestBed } from it('should select element by css', () => {
'@angular/core/testing'; let el = [Link]([Link]('.my-class'));
import { By } from '@angular/platform-browser'; expect(el).toBeTruthy();
expect([Link]).toEqual('my text');
describe('My component test', () => { });
let component: MyComponent;
let fixture: ComponentFixture<MyComponent>; it('should select all elements by css', () => {
let els = [Link]([Link]('.my-class'));
beforeEach(() => { expect([Link]).toEqual(2);
[Link]({ expect(els[0].[Link]).toEqual('my text 1');
declarations: [ MyComponent ] expect(els[1].[Link]).toEqual('my text 2');
}); });
});
fixture = [Link](MyComponent);
component = [Link];
[Link]();
});

802
import { ComponentFixture, TestBed } from it('should select element by directive', () => {
'@angular/core/testing'; let el =
import { By } from '@angular/platform-browser'; [Link]([Link](MyDirective));
import { MyDirective } from './[Link]'; expect(el).toBeTruthy();
});
describe('My component test', () => {
let component: MyComponent; it('should select all elements by directive', () => {
let fixture: ComponentFixture<MyComponent>; let els =
[Link]([Link](MyDirective));
beforeEach(() => { expect([Link]).toEqual(2);
[Link]({ });
declarations: [ MyComponent, MyDirective ] });
});

fixture = [Link](MyComponent);
component = [Link];
[Link]();
});

803
componentInstance
 A partir des fixtures on peut utiliser l’attribut componentInstance
afin de récupérer une instance de notre composant.

Exemple :
fixture = [Link](AppComponent);
const myCompo = [Link];

804
Premier Test d’un composant
En installant votre projet Angular vous avez un test avec votre premier
composant import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './[Link]';

describe('AppComponent', () => {
beforeEach(async(() => {
[Link]({
declarations: [
AppComponent
],
}).compileComponents();
}));

it('should create the app', () => {


const fixture = [Link](AppComponent);
const app = [Link];
expect(app).toBeTruthy();
});
805
Tester un composant
Déclencher un évènement
 Pour déclencher un évènement dans un élément du DOM, votre
DebugElement possède une méthode triggerEventHandler
 Elle prend en premier paramètre le nom de l’événement à
déclencher.
 En second elle s’attend à un objet event fictif. Ceci sert lorsque la
méthode qui sera déclenché à l’écoute de l’événement attend un
objet event. Si ce n’est pas le cas passez null.
 La méthode triggerEventHandler ne génère pas de bubbling,
l’event n’a comme porté que l’élément hôte.

806
Tester un composant
 Soit le composant counterComponent
Pour ce composant on peut tester les éléments suivants :
Lorsque l'utilisateur click sur le bouton ‘+’, le compteur s'incrémente
de 1.
 Lorsque l'utilisateur click sur le bouton ‘-’, le compteur se
décrémente de 1

[Link] 807
Tester un composant
 Commencez par tester la première Assertion
 Lorsque l'utilisateur click sur le bouton ‘+’, le compteur s'incrémente
de 1.
 Ici le Arrange est déjà prêt
 Le Act c’est le click sur le bouton increment
 Le Assert est la vérification que la valeur a augmenté de 1.

808
Tester un composant
 Les sélecteurs de type et de classe introduisent un couplage fort entre le test et le
template.
 Les éléments HTML sont choisis pour des raisons sémantiques. Les classes
sont choisies principalement pour le style.
 Les deux changent fréquemment lorsque le modèle du composant est refactorisé.
 Le test doit-il échouer si le type ou la classe de l'élément change ?
 Le test devrait mieux trouver l'élément par une caractéristique qui ne change
jamais et qui n'a pas de signification supplémentaire
 Un propriété identifiant l’élément à sélectionner est une bonne alternative

<button (click)="increment()" data-testid="increment-button">+</button>


809
Tester un composant
Factoriser notre code de tests
 Une fois le test de l’incrémentation terminé, discutons le test de la
décrémentation.
 Nous remarquons que le code va être très répétitif et on peut identifier plusieurs
éléments qu’on peut refaire dans plusieurs test.
1. Chercher un élément par son id, ici vous pouvez utilisez le pattern qui vous va,
juste mettez vous d’accord avec toute l’équipe
2. Déclencher une événement sur un élément sélectionné via son id
3. S’attendre à trouver un text dans un élément identifié par son id.
 La question qui se pose est : Pourquoi ne pas créer des helpers permettant de
centraliser tous les besoins redondants.

810
Tester un composant
Factoriser notre code de tests
 Vous pouvez créer vos helpers avec votre propre logique
 Vous pouvez vous inspirer des helpers de [Link]

[Link]

[Link] 811
Exercice
 Créer un composant ColorUtComponent.

 La couleur du background du paragraphe crée par défaut doit être


rouge. En cliquant sur le paragraphe, elle doit se changer en jaune.

Tester le fonctionnement de votre composant.

812
Exercice
 Créer un composant Authentification
 Dans ce composant on va avoir deux
div. @Component({
selector: 'app-authentification',
 La première div apparaitra si le user templateUrl: './[Link]',
est authentifié. styleUrls: ['./[Link]']
})
 La seconde sinon. export class AuthentificationComponent {
isLogged = true;
 Utiliser le service AuthService crée constructor(private authentificationService:
pour la phase de test. AuthentificationService) { }
ngOnInit() {
<div *ngIf="isLogged"> }
<h1>User Logged</h1> login() {
</div> [Link] =
[Link]();
<div *ngIf="!isLogged">
}
<h1>User not Logged</h1> }
</div> 813
Exercice
Que pensez vous de cette solution. Si elle ne vous plait pas proposez
une alternatives et implémentez la.

it('should display User logged when user is logged', () => {


[Link]('user', 'aymen');
[Link]();
[Link]();
expect([Link]('h1').textContent).toContain('User Logged');
});

814
Test Asynchrone
 Il existe plusieurs façons de gérer les tests asynchrones avec Jasmine
dans Angular.
 L'une des méthodes courantes consiste à utiliser des promesses pour
gérer les appels asynchrones, et à utiliser la fonction done() de Jasmine
pour signaler la fin des tests.
 Il y a aussi la fonction fakeAsync fournie par
@angular/core/testing qui permet de simuler l'exécution
asynchrone dans les tests.

815
Test Asynchrone
done
 La fonction done() de Jasmine permet de signaler la fin des tests.
 En la passant comme paramètre à votre fonction de test, vous
spécifier à jasmine que c’est un traitement asynchrone et qu’il doit
attendre votre signal pour lancer la vérification.
describe(‘Test Asynchrone', () => {
it('should do something async', (done: DoneFn) => {
asyncFunction().then(() => {
// Mettez votre assertion ici
done();
});
});
}); 816
Test Asynchrone
done
it('exemple timeout with done', (done: DoneFn) => {
let x = 1;
setTimeout(() => {
x++;
expect(x).toBe(2);
done();
}, 1000);
});

817
Exercice
 Créer un composant helloWorld.
 Ce composant va juste attendre une second puis afficher helloWorld
dans une div
 Tester ce composant

818
Test Asynchrone
fakeAsync
 fakeAsync est une fonction fournie par @angular/core/testing qui
permet de simuler l'exécution asynchrone dans les tests.
 Elle est utilisé avec la fonction tick() pour faire avancer le temps
virtuel et exécuter les tâches asynchrones planifiées.
 Cela permet de faciliter l'écriture des tests, car il n'est pas nécessaire
d'utiliser la fonction done() de Jasmine.
 Il est important de noter que lorsque vous utilisez fakeAsync, toutes
les fonctions asynchrones doivent être exécutées dans le contexte
de fakeAsync, sinon elles ne seront pas prises en compte par le
mécanisme de simulation de temps.

819
Test Asynchrone
fakeAsync
 Lorsque vous utilisez fakeAsync pour simuler l'exécution asynchrone dans
les tests, vous pouvez utiliser plusieurs fonctions pour gérer le temps virtuel :
tick(milliseconds: number) : Cette fonction permet de faire avancer le temps
virtuel et d'exécuter les tâches asynchrones planifiées dans un intervalle de temps
donné (en millisecondes). Cela permet de vous assurer que toutes les tâches
asynchrones ont été exécutées dans un intervalle de temps donné avant de
continuer le test.
flush() : Cette fonction permet de faire avancer le temps virtuel et d'exécuter
toutes les tâches asynchrones planifiées, comme les timeouts et les intervals. Cela
permet de vous assurer que toutes les tâches asynchrones ont été exécutées avant
de continuer le test.
flushMicrotasks() : Cette fonction permet de faire avancer le temps virtuel et
d'exécuter toutes les tâches asynchrones de microtâches planifiées, comme les
promesses et les observables. Cela permet de vous assurer que toutes les
microtâches asynchrones ont été exécutées avant de continuer le test.

820
Test Asynchrone
fakeAsync
it('exemple timeout fakeAsync + tick()', fakeAsync(() => {
let x = 1;
setTimeout(() => {
x++;
}, 1000);
tick(1000);
expect(x).toBe(2);
}));

821
Test Asynchrone
fakeAsync
it('exemple timeout fakeAsync + flush', fakeAsync(() => {
let x = 1;
setTimeout(() => {
x++;
}, 1000);
flush();
expect(x).toBe(2);
}));

822
Exercice
 Reprenez ce même exercice en ajoutant un test avec async
 Tester le fait qu’avant 1s le h1 n’existe pas.

823
Test Asynchrone
async
 Permet de gérer les tests des opérations asynchrones
 Ne possède pas les fonctions de gestion de temps comme tick ou
flush,…
 Pour gérer la fin des opération asynchrones utiliser la fonction
whenStable de votre fixture.
 Cette fonction retourne une promesse vous permettant d’exécuter un
traitement que votre code est stable.
 On l’utilise par exemple dans les très rares cas ou on veut tester du
http, on ne sait pas quand ca termine donc on ne peut pas faire du
fakeAsync.

824
Code Coverage
 Afin d’obtenir un rapport de code coverage lancer la commande
suivant : ng test --no-watch --code-coverage
 Lorsque les tests sont terminés, la commande crée un nouveau
répertoire /coverage dans le projet. Ouvrez le fichier [Link] pour
afficher un rapport avec votre code source et les valeurs de
couverture de code.

825
Tests E2E
Ces test permettent de SIMULER L'UTILISATION RÉELLE de
votre application.
 Certains tests ont une vue d'ensemble de haut niveau sur
l'application.
 Ils simulent un utilisateur interagissant avec l'application :
navigation vers une adresse,
lecture de texte,
clic sur un lien ou un bouton,
remplissage d'un formulaire,
déplacement de la souris ou saisie au clavier.
826
Tests E2E
 Ces tests font des attentes sur ce que l'utilisateur voit.
 Du point de vue de l'utilisateur, peu importe que votre application
soit implémentée dans Angular.
L'expérience complète est testée => TESTS DE BOUT EN
BOUT
Les tests de bout en bout constituent également la partie automatisée
des tests d'acceptation puisqu'ils indiquent si l'application fonctionne
pour l'utilisateur.

827
Tests E2E
Comment ca marche ?
 Les tests E2E vont donc simuler les interactions de l’utilisateur
avec votre application.
 Vous allez donc lancer le navigateur, et le contrôler afin de
simuler un scénario d’interactions.
 Une fois le scénario exécuté, vous allez avoir des attentes
(expectations), exactement comme avec les tests unitaires :
 Est-ce que les éléments de la pages sont correct
 Est-ce que suite au click j’ai le bon affichage
…

828
Tests E2E
Cypress
 Cypress est un Framework pour les Test E2E dont les avantages sont :
1. Interface utilisateur facile à utiliser
2. Temps de développement rapide
3. Intégration parfaite avec le développement front-end
4. Exécution rapide des tests
5. Capacité à tester directement dans le navigateur
6. Possibilité de deboguer facilement les tests
7. Prise en charge native de la manipulation du DOM et de l'Ajax
8. Documentations et communauté actives
9. Tests fiables et reproductibles.

829
Tests E2E
Cypress
 Afin d’installer Cypress utiliser la commande npm i cypress –save-dev.
 Avec angular, utilisez la commande ng add @cypress/schematic
 L’utilisation de cette commande permet d’automatiser la configuration
en ajoutant
 Cypress et les packages npm auxiliaires à [Link].
 Le fichier de configuration Cypress [Link].
 Modifiez le fichier de configuration [Link] afin d'ajouter des
commandes d'exécution ng.
Créez un sous-répertoire nommé cypress avec des templates pour
vos tests.
830
Exercice
 Installez Cypress et regarder les différents fichiers ajoutés

831
Tests E2E
Le dossier cypress
 Le dossier cypress généré contient :
 Une configuration [Link] pour tous les
fichiers TypeScript spécifiquement dans ce
répertoire,
 Un répertoire e2e pour les tests E2E,
 Un répertoire de support pour les commandes
personnalisées et autres assistants de test,
 un répertoire fixtures pour les données de
test.

832
Tests E2E
Configuration
 Il y a aussi le fichier [Link] au niveau de
la racine de votre projet.
 Ce fichier vous permet de configurer cypress
import { defineConfig } from 'cypress'
export default defineConfig({
e2e: {
'baseUrl': '[Link]
},
component: {
devServer: {
framework: 'angular',
bundler: 'webpack',
},
specPattern: '**/*.[Link]'
}
}) 833
Tests E2E
Lancer les tests E2E
Dans [Link] on peut identifier deux commandes:
 cypress:open qui exécute la commande cypress open
 cypress:run qui exécute la commande cypress run

834
Tests E2E
Lancer les tests E2E
cypress open est le mode interactif. Elle ouvre une fenêtre
dans laquelle vous pouvez sélectionner le navigateur à utiliser et
les tests à exécuter. A chaque changement, tout est mis à jour.
 cypress run, c’est le mode non interactif. Exécute les tests
dans un navigateur "headless". Cela signifie que la fenêtre du
navigateur n'est pas visible. Les tests sont exécutés une fois,
puis le navigateur est fermé et la commande shell se termine.
Cette commande est généralement utilisée dans un
environnement d'intégration continue.
835
Exercice
 Lancez cypress en mode interactif et suivez les étapes

836
Tests E2E
Ecrire des tests E2E
 Vous devez lancer votre serveur dans un terminal et cypress dans l’autre
 Vos tests doivent être dans le dossier e2e
 Chaque groupement de test, généralement par page sera représenté par un
fichier dont l’extension est .[Link].
En règle générale, un fichier contient un bloc de description describe.
On peut avoir des blocs de description imbriqués.
À l'intérieur, les blocs beforeEach, afterEach, beforeAll, afterAll peuvent
être utilisés de la même manière que les tests Jasmine.
À l'intérieur des blocs on peut avoir un ou plusieurs attentes.
837
Tests E2E
Visiter une page
 Afin d’accéder à une page vous pouvez utiliser visit
 Si vous avez définit votre baseUrl dans la config comme nous l’avons spécifié
(Qui est une bonne pratique : [Link]
practices#Setting-a-global-baseUrl), ajoutez l’URI vers lequel vous voulez
naviguer.
[Link]('/') // visits the baseUrl
[Link]('[Link]') // visits the local file "[Link]" if baseUrl is null
[Link]('[Link] // specify full URL if baseUrl is null or the domain is different
//the baseUrl
[Link]({
url: '/pages/[Link]',
method: 'GET',
})

[Link] 838
Tests E2E
Sélectionner des éléments
Certaines méthodes joue le double rôle de sélecteur et d’assertions comme
get qui vérifie que l’élément existe et qui le sélectionne
 [Link](): Cette méthode permet de sélectionner un élément spécifique en
utilisant un sélecteur CSS. Exemple: [Link]('#bouton-submit').click()
 [Link](): Cette méthode permet de sélectionner un élément en
fonction du texte qu'ils contient.
Exemple: [Link]('Submit').click()
 [Link](): Cette méthode permet de sélectionner l'élément qui a le focus
actuellement.
Exemple: [Link]().should('[Link]', 'form-input-focused')
[Link] 839
Tests E2E
Sélectionner des éléments
[Link](): Cette méthode permet de sélectionner le premier élément d'une
liste d'éléments.
Exemple: [Link]('.liste-éléments').first()
 [Link](): Cette méthode permet de sélectionner le dernier élément d'une liste
d'éléments.
Exemple: [Link]('.liste-éléments').last()
[Link](): Cette méthode permet de sélectionner le parent d'un élément
donné.
Exemple: [Link]('.élément-enfant').parent()

[Link] 840
Tests E2E
Sélectionner des éléments
[Link](): Cette méthode permet de sélectionner la racine du document
HTML. Exemple: [Link]().should('[Link]', 'racine')
[Link](): Cette méthode permet de sélectionner les enfants d'un élément
donné. Exemple: [Link]('.élément-parent').children().should('[Link]', '3')
[Link](): Cette méthode permet de sélectionner l'élément suivant d'un
élément donné.
Exemple: [Link]('.élément-précédent').next()
[Link](): Cette méthode permet de sélectionner l'élément précédent d'un
élément donné. Exemple: `[Link]('.élément-suivant').prev().

[Link] 841
Tests E2E
Sélectionner des éléments
Les bonnes pratiques
 Cypress déconseille d’utiliser les sélecteurs susceptibles d’être modifiés
fréquemment comme les classes ou les ids c’est un Anti-Pattern.
 La bonne pratique est d’utiliser des attributs avec ce pattern data-*
permettant de donner un context à vos sélecteurs et de les isoler des
changements css et js.
 De plus en utilisant cette technique le selector Playground de cypress va
préférer ces sélecteurs et les mettre en avant :
data-cy
data-test
data-testid

[Link] 842
Exercice
 Dans la page Cv, vérifiez l’existence de la liste des cvs.
 Vérifiez qu’il n’existe pas de cvCard au départ (utiliser l’assertion
.should(‘[Link]’)).

843
Tests E2E
Test des requêtes HTTP
 Lorsque vous testez des apis, vous avez deux stratégies:
 Utilisez la réponse du serveur : la stratégie de requêtes réelles consiste à utiliser
les API externes pour effectuer des tests. Cela signifie que les tests sont plus
proches de la réalité, mais peuvent être plus lents et plus instables en raison
de la dépendance aux API. Cependant, cette approche garantit une meilleure
couverture des cas d'utilisation et une meilleure qualité de test en général.
 Utilisez des fixtures : : La stratégie de requêtes mockées consiste à
remplacer les réponses API réelles par des réponses prédéfinies et contrôlées
par le développeur. Cela signifie que les tests ne dépendent pas de la
disponibilité ou de la rapidité des API, ce qui peut accélérer les tests et les
rendre plus fiables. Cependant, cette approche n'est pas toujours réaliste et peut
ne pas couvrir tous les cas d'utilisation possibles.

[Link] 844
Tests E2E
Test des requêtes HTTP
API Réel
 Avantages
 Plus susceptible de travailler en production
 Tester la couverture de vos endpoints
 Idéal pour le rendu HTML traditionnel côté serveur (en cas de réponse HTML et non JSON)

 Inconvénients
 Nécessite de seeder des données (Base de données de test à préparer pour les différents cas)
 Beaucoup plus lent
 Plus difficile à tester les cas extrêmes

 Utilisation suggérée
 Utiliser avec parcimonie
 Idéal pour les chemins critiques de votre application

[Link] 845
Tests E2E
Test des requêtes HTTP
API Mockés
Avantages
 Contrôle des corps de réponse, de l'état et des en-têtes
 Peut forcer les réponses à prendre plus de temps pour simuler le retard du réseau
Temps de réponse rapides, < 20 ms
Inconvénients
Aucune garantie que vos réponses tronquées correspondent aux données réelles envoyées par le
serveur
Aucun test couverture sur certains points de terminaison de serveur
Pas aussi utile si vous utilisez le rendu HTML traditionnel côté serveur
Utilisation suggérée
Utilisez pour la grande majorité des tests
Mélangez et faites correspondre, ayez généralement un vrai test de bout en bout, puis remplacez
le reste
Parfait pour JSON Apis
[Link] 846
Tests E2E
Test des requêtes HTTP
API Mockés
 Cypress vous permet de remplacer une réponse et de contrôler le
corps, l'état, les en-têtes ou même le délai.
 [Link]() est utilisé pour contrôler le comportement des requêtes
HTTP. Vous pouvez définir de manière statique le corps, le status
HTTP, les en-têtes et d'autres caractéristiques de réponse.
 Elle peut prend en paramètre un grand nombre de combinaison
selon votre cas d’utilisation.

[Link] 847
Tests E2E
Test des requêtes HTTP
API Mockés
spying
[Link]('/users/**')
[Link]('GET', '/users ')
[Link]({
method: 'GET’,
url: '/users ’,
hostname: 'localhost',
})
spying and response stubbing
[Link]('POST', '/users ', {
statusCode: 201,
body: {
name: 'Peter Pan’,
},
})
spying, dynamic stubbing, request modification, etc.
[Link]('/users ', { hostname: 'localhost' }, (req) {
do something with request and/or response
})

[Link] 848
Tests E2E
Test des requêtes HTTP
API Mockés / intercept, mockez une réponse
 Lorsque vous utilisez intercept suivez les étapes suivantes:
1. Préparer l’interception
2. Lancer l’opération souhaité
3. Lancez vos Assertions

[Link] 849
Tests E2E
Test des requêtes HTTP
API Mockés / intercept, mockez une réponse
 Vous pouvez moquer la réponse de votre api avec des fixtures.
 Les fixtures peuvent êtres de plusieurs types et vous avez le dossier fixtures pour
les stocker.
// requests to '/update' will be fulfilled [Link]('/not-found', { [Link](
// with a body of "success" statusCode: 404, {
[Link]('/update', 'success') body: '404 Not Found!', method: 'GET',
// requests to '/[Link]' will be fulfilled headers: { url: [Link],
// with the contents of the "[Link]" fixture 'x-not-found': 'true', },
[Link]('/[Link]', { fixture: '[Link]' }) }, {
[Link]('/projects', { }) fixture: 'cvs',
body: [{ projectId: '1' }, { projectId: '2' }], }
}) )

[Link] 850
Tests E2E
Test des requêtes HTTP
API Mockés / intercept, affecter un alias
 Afin de pouvoir manipuler l’intercept, comme par exemple l’attendre
avec un wait, vous pouvez lui affecter un alias.
[Link]('[Link]
[Link]('@getSettings')
[Link]({
url: '[Link]
query: { q: 'expected terms' },
}).as('search')
[Link]('@search')

[Link] 851
Exercice
 Faite en sorte d’avoir des fixtures pour la liste des cvs permettant de
tester cette liste.
 Vérifier que l’affichage utilise vos fixtures
 Ajouter des fixture pour la sélection d’un cv par son id.

852
Tests E2E
Les assertions
 Cypress intègre plusieurs assertions de diverses bibliothèques
d'assertions JS telles que Chai, jQuery, etc.
 Nous pouvons globalement classer toutes ces assertions en deux
segments en fonction du sujet sur lequel nous pouvons les invoquer :
 Les assertions implicites
 Les assertions explicites

853
Tests E2E
Les assertions implicites
 Lorsque l'assertion s'applique à l'objet fourni par la commande chaînée
parente, elle s'appelle une assertion implicite.
 Cette catégorie d'assertions inclut généralement des commandes telles que
".should()" et ".and()".
 Comme ces commandes ne sont pas indépendantes et dépendent toujours
de la commande parente précédemment chaînée, elles héritent et agissent
automatiquement sur l'objet généré par la commande précédente.
Généralement, nous utilisons des assertions implicites lorsque nous voulons :
 Affirmer plusieurs validations sur le même sujet.
 Changez de sujet avant de faire des affirmations sur le sujet.

[Link] 854
<table class="table table-bordered assertion-table">
<thead>

Tests E2E <tr><th>#</th><th>Column heading</th><th>Column heading</th></tr>


</thead>
<tbody>

Les assertions <tr><th scope="row">1</th><td>Column content</td><td>Column content</td></tr>


<tr><th scope="row">2</th><td>Column content</td><td>Column content</td></tr>
<tr class="success"><th scope="row">3</th><td>Column content</td><td>Column
content</td></tr>
</tbody>
</table>

[Link]('.assertion-table')
.find('tbody tr:last')
.should('[Link]', 'success')
.find('td') // Pour vérifier qu'un texte valide une expression régulière,
.first() // préférer l'utilisation de contains
// valider le contenu d'un élément [Link]('.assertion-table')
.should('[Link]', 'Column content') .find('tbody tr:last')
.should('contain', 'Column content') // finds first element with text content matching regular
.should('[Link]', 'Column content') expression
.should('match', 'td') .contains('td', /column content/i)
.should('[Link]')

[Link] 855
Tests E2E
Les assertions
have
 exist : pour vérifier l'existence d'un élément
[Link] : pour vérifier la visibilité d'un élément
[Link] : pour vérifier l'état activé/désactivé d'un élément
[Link] : pour vérifier l'état coché/décoché d'un élément
[Link] : pour vérifier la valeur d'un élément
[Link] : pour vérifier le texte d'un élément
[Link] : pour vérifier la présence d'un attribut de style sur un
élément
[Link] : pour vérifier la présence d'une classe sur un élément
[Link] 856
Tests E2E
Les assertions
[Link] : pour vérifier la longueur d'un objet.
[Link] / [Link] : pour vérifier si une expression est vraie ou fausse
 eq / equal / eql : pour vérifier l'égalité de deux valeurs
 contain : pour vérifier la présence d'une valeur dans un tableau ou une
chaîne de caractères
 match : pour vérifier si une chaîne de caractères correspond à une
expression régulière
 [Link] / [Link] : pour vérifier si une valeur est
supérieure ou inférieure à une autre valeur.

[Link] 857
Exercice
 Dans la page des cvs vous avez deux onglets, un pour les seniors et un
pour les juniors.
 Vérifiez que vous avez les deux onglets
 Vérifiez que le premiers élément correspond pour les deux listes
 Vérifiez que La taille des deux listes est correcte.
 Vérifiez que le premier onglet et visible et que le second ne l’est pas

858
Tests E2E
Les assertions explicites
 Lorsqu'il est nécessaire de transmettre un sujet explicite à l'assertion, celle-ci
relève de la catégorie d'assertion explicite.
 Cette catégorie d'assertions contient les commandes telles que "expect()" et
"assert()", qui permettent de passer un sujet explicitement à l’assertion.
 En règle générale, vous utiliserez des "assertions explicites" lorsque vous
souhaitez :
 Effectuez une logique personnalisée avant de faire les affirmations sur le sujet
donné.
 Effectuez plusieurs assertions sur le même sujet.
 Cependant les deux types d’assertions peuvent être utilisées

[Link] 859
Tests E2E
Les assertions
[Link]('div').should(($div) {
expect($div).[Link](1)

const className = $div[0].className

className will be a string like "main abc123 heading xyz987"


expect(className).[Link](/heading /)
})

860
Tests E2E
Les assertions
 Pour ces assertions Cypress regroupe la célèbre bibliothèque d'assertions
Chai, ainsi que ses extensions comme jQuery
Chainers Assertion
attr(name, [value]) expect($el).[Link]('foo', 'bar')
prop(name, [value]) expect($el).[Link]('disabled', false)
css(name, [value]) expect($el).[Link]('background-color', 'rgb(0, 0, 0)')
data(name, [value]) expect($el).[Link]('foo', 'bar')
class(className) expect($el).[Link]('foo')
id(id) expect($el).[Link]('foo')

[Link] 861
Tests E2E
Les assertions
Chainers Assertion
html(html) expect($el).[Link]('I love testing')
text(text) expect($el).[Link]('I love testing')
value(value) expect($el).[Link]('test@[Link]')
visible expect($el).[Link]
hidden expect($el).[Link]
selected expect($option).[Link]
checked expect($input).[Link]
expect($input).[Link]
focus[ed]
expect($input).[Link]
[Link] 862
Tests E2E
Les assertions
Chainers Assertion
enabled expect($input).[Link]
disabled expect($input).[Link]
empty expect($el).[Link]
exist expect($nonexistent).[Link]
match(selector) expect($emptyEl).[Link](':empty')
contain(text) expect($el).[Link]('text')
descendants(selector) expect($el).[Link]('div')

[Link] 863
Tests E2E
Location
 Afin d’avoir des information sur la localisation
actuelle, donc l’url actif, vous pouvez utilisez la
commande location.
 Avec l’assertion should, vous pouvez lui passez une
callback qui prend en paramètre la location et appelle
les expections que vous voulez valider.

[Link]().should((location) => {
expect([Link]).[Link]('/cv/1');
});

864
Tests E2E
Déclencher des actions
 Cypress vous permet de simuler des fonctions.
 Pour écrire dans un élément DOM, utilisez la commande .type().
 Vous pouvez effacer le champ avant de taper avec clear()
it('Visits the initial project page', () => {
[Link]('/');
[Link]('Faurecia');
[Link]('[data-cy=email-input]')
.type('aymen@[Link]')
.should('[Link]', 'aymen@[Link]')
});
[Link] 865
Tests E2E
Déclencher des actions
 Pour avoir le focus sur un élément du DOM, utilisez la commande focus()
 Pour perdre le focus sur un élément du DOM, utilisez la commande blur()
 Pour soumettre un formulaire, utilisez la commande [Link]()
 Pour cliquer sur un élément du DOM, utilisez la commande click(). Si
l’élément n’est pas visible ou n’est pas enabled ajouter en paramètre
{force:true} .click({ force: true });

 Pour cocher une case ou une radio, utilisez la commande check().


 Pour sélectionner une option dans un select, utilisez la commande select().

[Link] 866
Tests E2E
Déclencher des actions
 Afin de simuler le click sur un caractère spécial, entre, insert, delete, utilisez
la syntaxe suivante:
// Special characters: // Arrows:
[Link]('input').type('{enter}') [Link]('input').type('{upArrow}')
[Link]('input').type('{backspace}') [Link]('input').type('{downArrow}')
[Link]('input').type('{del}') [Link]('input').type('{leftArrow}')
[Link]('input').type('{esc}') [Link]('input').type('{rightArrow}')
[Link]('input').type('{end}')
[Link]('input').type('{home}')
[Link]('input').type('{insert}') // Modifier keys:
[Link]('input').type('{moveToEnd}') // Move cursor to the end of [Link]('input').type('{shift}')
typeable element [Link]('input').type('{ctrl}')
[Link]('input').type('{moveToStart}') // Move cursor to the start of [Link]('input').type('{alt}')
typeable element
[Link]('input').type('{pageDown}') // Scroll down
[Link]('input').type('{pageUp}') // Scroll up
[Link]('input').type('{selectAll}') // Select the entire input value 867
Exercice
 Ecrivez un test qui permet de tester le color component
 Au début la couleur de la div doit être celle de l’uri
 Lorsqu’on écrit dans l’input la couleur de la div change
 Lorsqu’on appuie sur reset la couleur reprend la couleur par défaut

868
Exercice
 Ecrivez un test qui permet de tester le fonctionnement de la sélection
d’un cv via le click sur le bouton détails.
 Utilisez des fixtures
 Simulez un click sur le bouton détails du premier élément.
 Vérifiez qu’en cliquant sur le bouton, vous accéder à la bonne url
 Vérifier que les infos du cv sont bien affichées

869
PWA
 Les Progressive Web Apps (PWA) sont des applications Web qui utilisent
les dernières technologies pour se rapprocher de la fiabilité et des
performances des applications mobiles natives.
Les PWA peuvent s’installer sur la tablette ou le mobile de l’utilisateur et
possèdent leur propre icône, sans avoir besoin de passer par un app store.
Elles fonctionnent sur tous les supports, elles sont progressives et responsives.
 Elles se lancent en plein écran et peuvent émettre des notifications push.
 Elles sont indexables par les moteurs de recherche, contrairement aux
applications natives, elles sont donc plus simple à trouver.
 Elles sont partageables avec un lien sans installation (contrairement à une
application mobile).
870
PWA
Les services workers
 L’une des technologies utilisée par les PWA sont les service workers. Ce
sont de scripts qui tournent dans les navigateurs et sont responsables de
la mise en cache des ressources nécessaires à une application.
 Ils fonctionnent comme un proxy du côté du navigateur : ils
interceptent toutes les requêtes HTTP sortantes effectuées par
l’application et décident de comment y répondre (Passer par le cache ou
utiliser le réseau).
 Les service workers sont toujours actifs même après la fermeture d’un
onglet.

871
PWA
Les services Workers
 Quand elles sont lancées depuis l’écran de l’utilisateur, les services workers
permettent aux PWA de se charger instantanément, et ce même hors
ligne.
 Elles sont à jour grâce aux services workers.
 Disponibles en production uniquement en HTTPS et en développement
sur localhost.
 Les avantages en termes de performances de l'installation de tous nos
bundles Javascript et CSS sur le navigateur de l'utilisateur, c’est qu’elles
rendent le démarrage de l'application beaucoup plus rapide.

872
Les services workers dans Angular
 Depuis sa version 5, Angular nous propose sa propre API pour gérer les
service workers.
 Le Service Worker d’Angular peut mettre en cache toutes sortes de
contenus dans le Cache Storage du navigateur.
 Le service worker Angular est comme un cache installé du côté du
navigateur de l’utilisateur. Son objectif est de répondre aux requêtes
effectuées par l’application Angular pour obtenir des ressources ou des
données grâce au cache local.
 Comme tous les caches, il y a des règles paramétrables pour décider
comment le système de cache fonctionne.

873
Les services workers dans Angular
 Le service worker Angular est implémenté de la manière suivante :
 Lors du premier chargement de l’application, il met en cache les
parties de l’application configurées pour être cachées.
 Lorsque l’utilisateur rafraîchit l’application, il obtient la dernière
version de l’application mise en cache.
 La mise à jour de l’application est faite en arrière plan, pendant
que l’utilisateur utilise la version précédente qui est en cache. Lorsque
la nouvelle version est totalement chargée, la mise à jour est prête.
 Lorsque l’utilisateur rechargera une nouvelle fois l’application il
aura la dernière version en cache et il obtiendra la version mise à
jour.
874
Les services workers dans Angular
 Pour obtenir ce comportement, le service worker Angular charge un
fichier [Link] (angular pour ng, et service worker pour sw) depuis
le serveur. Ce fichier décrit les ressources à mettre en cache et permet
de savoir la version de tous les fichiers mis en cache.
 Lorsque l’application est mise à jour, le contenu de ce fichier
change, et le service worker est informé qu’une nouvelle version de
l’application est disponible et doit être téléchargée et mise en
cache.
 Ce fichier est généré à partir d’un fichier de configuration ngsw-
[Link].

875
Les services workers dans Angular
1er chargement de l’application
1- http Request 2- http Request

Angular App 3- http Response Service Worker 3- http Response


Server

Cache Storage

876
Les services workers dans Angular
n éme chargement
1- http Request Donne moi le fichier

[Link]
Angular App Service Worker Server
3- Version en cache
de l’application

Cache Storage
Si le fichier [Link] récupéré au
niveau du serveur est modifié, je met
à jour les données du cache

877
Les services workers dans Angular
 Afin de mettre en place votre Service worker utiliser la commande
ng add @angular/pwa --project NOM_DU_PROJET
Cette commande va permettre d’effectuer les actions suivantes :
Ajouter la dépendance @angular/service-worker
Activer le support du service worker par le CLI
Importer et activer le service worker dans le module racine [Link]
Mets à jour le fichier [Link] pour importer le [Link] et des
métas tags pour les couleurs
Créer les fichiers d’icônes pour pouvoir ajouter la PWA à l’écran d’accueil sur mobile
Crée le fichier [Link] qui permet notamment de configurer la mise en
cache
[Link] 878
Exercice
 Installer le module pwa d’Angular
 Builder votre projet via ng build
 Si vous avez le serveur http-client installé, utiliser le pour tester votre
application. Sinon installer le via la commande npm i -g http-server
 Lancer la commande http-server –c-1 dist/nomDeVotreProjet
 Accéder à l’url localhost:8080, vérifier que ca marche puis ouvrez le
devtools de votre navigateur.
 Allez dans l’onglet application et vérifier les éléments Manifest et Service
Workers.
 Mettez vous en mode offline et vérifier que votre application marche
encore.

879
Exercice

880
Exercice
 Visiter votre dossier de build et vérifier les changements qui ont été
réalisés

881
Les services workers
[Link]
 Le fichier [Link] permet de spécifier quels fichiers et
quelles données provenant des URLs spécifiées doivent être mise en
cache par le service worker et également comment ils doivent être mis à
jour.
 Ce fichier est ensuite utilisé par le CLI pendant le build en production (ng
build).
 [Link] utilise le format JSON. Tous les chemins de fichiers
doivent commencer par /, qui correspond au répertoire de déploiement —
généralement dist/<nom-projet> dans les projets CLI.
 Les modèles utilisent un format limité qui sera converti en interne en
regex
[Link] 882
Les services workers
[Link]
** permet de matcher 0 ou plusieurs niveaux de chemin (
/niv1/niv2/../nivn).
 ? Correspond exactement à un caractère à l'exception de /
 ! Marque le modèle comme étant négatif, ce qui signifie que seuls les
fichiers qui ne correspondent pas au modèle sont inclus.
* permet de matcher 0 ou plusieurs caractères en excluant /.
/**/*.html spécifie tous les fichiers HTML
/*.html spécifie uniquement les fichiers HTML à la racine

[Link] 883
{
"$schema": "./node_modules/@angular/service-worker/config/[Link]",
"index": "/[Link]",
"assetGroups": [
{
"name": "app",
"installMode": "prefetch",
"resources": {
"files": [
"/[Link]", "/[Link]", "/[Link]", "/*.css", "/*.js"
]
}
},
{
"name": "assets",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
"files": [
"/assets/**",
"/*.(svg|cur|jpg|jpeg|png|apng|webp|avif|gif|otf|ttf|woff|woff2)"
]
}
}
]
}
884
Les services workers
[Link]
Les AssetGroups
 Les AssetGroups sont les ressources qui sont mises à interface AssetGroup {
name: string;
jour en même temps que l’application. installMode?: 'prefetch'| 'lazy';
updateMode?: 'prefetch' | 'lazy';
La propriété assetGroups est un tableau qui contient resources: {
files?: string[];
des asset groups, qui décrivent un type de ressources urls?: string[];
et la stratégie de mise en cache. };
cacheQueryOptions?: {
ignoreSearch?: boolean;
 Elles peuvent inclure des ressources chargées depuis };
}
le serveur, depuis des CDNs ou autres sources externes.
Vous pouvez utiliser les regex pour matcher des URLs
dont seule une partie peut être connue par avance.

885
Les services workers
[Link]
Les assetGroups - installMode
Le mode d’installation, installMode, permet de paramétrer la
stratégie de mise en cache initiale d’une ressource.
 Il existe deux types de stratégies de mise en cache :
 prefetch : permet de télécharger toutes les ressources listées pendant
que la version actuelle de l’application est mise en cache. Cette
stratégie permet de s’assurer que toutes les ressources listées seront
disponibles, même hors connexion.
 lazy : les ressources ne sont mise en cache que lorsque des requêtes
pour les charger sont émises.

886
Les services workers
[Link]
Les assetGroups - updateMode
Le mode de mise à jour, updateMode, permet de paramétrer la stratégie
de mise à jour des ressources mises en cache lorsqu’une nouvelle
version de l’application est disponible.
Il existe deux types de stratégies de mise à jour des ressources qui ont
changé depuis la dernière version de l’application :
 prefetch : permet de dire au service worker de télécharger et de mettre
en cache les ressources qui ont été modifiées immédiatement.
 lazy : permet de dire au service worker d’attendre que ces ressources
soient requêtées pour les télécharger de nouveau afin de les mettre à
jour dans le cache. Pour activer ce mode de mise à jour, il est nécessaire
que le mode d’installation soit aussi lazy.
887
Les services workers
[Link]
Les assetGroups - ressources
Les ressources peuvent être de deux types.
Les fichiers, files, sont une liste de regex qui match un ou plusieurs
fichiers dans le dossier du build (public ou dist généralement).
Les urls sont une liste de regex qui match des urls lors de l’exécution
de l’application. Elles permettent par exemple de mettre en cache des
polices de caractères. "resources": {
"files": [
"/[Link]",
"/[Link]",
"/[Link]",
"/*.css",
"/*.js"
]
}
888
Les services workers
[Link]
Le fichier [Link] d'une application Web fournit des
informations la concernant dans un fichier JSON afin de pouvoir
l'installer sur l'écran d'accueil d'un appareil mobile.
Les propriétés sont :
 Le name ou le short_name sera le nom utilisé sur l’écran d’accueil du
mobile sous l’icône de votre application une fois que celle-ci aura été installée.
Le theme_color définit la couleur du thème de l’application. Cette couleur
sera utilisée à certains endroits par le système d’exploitation (Android par
exemple, va utiliser cette couleur dans le gestionnaire des applications lancées).

889
Les services workers
[Link]
 background_color est la couleur de fond de base de l’application web.
Cette valeur peut être utilisée par le navigateur comme couleur de
fond lorsque le fichier [Link] est disponible avant
que la feuille de style de l’application soit chargée.
 display est le mode d’affichage de l’application web sur mobile
lorsqu’elle est installée. La valeur recommandée est standalone pour
la plupart des utilisations. L’application ressemblera à une
application native et se comportera comme telle
([Link]
 icons sont les images qui vont servir d’icônes pour l’application
dans différents tailles sur l’appareil mobile ou la tablette.
890
Les services workers
Le dossier Build
[Link]
 Ce fichier est généré à chaque build du projet.
 Il s'agit du fichier de configuration d'exécution que le Service Worker
d’Angular utilise.
 Ce fichier est construit sur la base du fichier [Link] et
contient toutes les informations nécessaires au Service Worker
d’Angular pour savoir au moment de l'exécution quels fichiers il
doit mettre en cache et quand.
 C’est une version étendue du fichier [Link], où toutes
les URL génériques ont été appliquées et remplacées par les
chemins de tous les fichiers qui leur correspondaient.

891
{
"configVersion": 1,
"index": "/[Link]",
"assetGroups": [
{
"name": "app", …
"urls": [
"/[Link]",
"/[Link]", …]
},
{
"name": "assets",…
"urls": [
"/assets/icons/[Link]",
"/assets/icons/[Link]", …
]
}
],
"hashTable": {
"/assets/icons/[Link]": "b1f06c7d714abb4a0cc4d1f7c954feb26826e4c4",
"/assets/icons/[Link]": "96a0d629765c1bc85d9263c6bc83094ca776d267",

},
} 892
Les services workers
Le service swUpdate
Le service SwUpdate permet d’accéder à plusieurs événements
qui indiquent le moment où le service worker a trouvé une mise à
jour disponible de l’application ou lorsqu’il a activé une mise à
jour, c’est-à-dire lorsqu’il sert le contenu de l’application mise à jour.
Ce service permet trois opérations :
 D’être notifié lorsqu’une mise à jour est disponible : c’est-à-dire une nouvelle
version de l’application qui sera chargée lors du rafraichissement de la page.
 Demander au service worker de vérifier si une mise à jour est disponible.
 Demander au service worker d’activer la dernière version

893
Les services workers
Le service swUpdate
versionUpdate
La propriété versionUpdates est un observable de SwUpdate et qui
émet quatre événements :
Émis lorsque le service worker a détecté une nouvelle version de l'application sur le
VersionDetectedEvent
serveur et est sur le point de commencer à la télécharger.

Émis lorsque le service worker a vérifié la version de l'application sur le serveur et


NoNewVersionDetectedEvent
n'a pas trouvé de nouvelle version.

Émis lorsqu'une nouvelle version de l'application est disponible pour être activée par les
VersionReadyEvent clients. Il peut être utilisé pour informer l'utilisateur d'une mise à jour disponible ou l'inviter
à actualiser la page.

Émis lorsque l'installation d'une nouvelle version a échoué. Il peut être utilisé à des fins de
VersionInstallationFailedEvent
journalisation/surveillance.

894
Exercice
 Faites en sorte d’avoir une alerte lors de la détection d’une nouvelle
version de votre application.
 Cette alerte devra notifier l’utilisateur qu’une nouvelle version de
l’application a été détecté et s’il veut recharger la page.

895
Les services workers
[Link]
Les dataGroups
 Les dataGroups export interface DataGroup {
permettent de mettre en name: string;
urls: string[];
cache la réponse de version?: number;
requêtes à des APIs. cacheConfig: {
maxSize: number;
 Les requêtes pour des maxAge: string;
données ne sont pas timeout?: string;
strategy?: 'freshness' | 'performance';
versionnées comme les };
assets. Elles sont mises en cacheQueryOptions?: {
ignoreSearch?: boolean;
cache suivant des };
paramètres configurés }
manuellement.
896
Les services workers
[Link]
Les dataGroups
name, permet d’identifier chaque dataGroup de manière unique.
urls contient un tableau avec des regex qui vont permettre la mise en
cache des réponses des requêtes aux URLs qui matcheront ces regex.
version permet d’indiquer que le format des données de l’API a
changé et que l’ancien n’est plus compatible avec l’application. Il permet
donc d’indiquer au service worker qu’il est nécessaire de ne pas utiliser
les données mises en cache depuis l’API. Cette propriété est un nombre
qui s’incrémente manuellement en partant de 0.

897
Les services workers
[Link]
Les dataGroups
maxSize correspond au nombre maximal de réponses d’API en cache.
maxAge permet d’indiquer combien de temps les données mises en cache
peuvent être conservées. Elle se paramètre par exemple : 2d12h (avec d pour days,
h, m, s et u pour heures, minutes, secondes et millisecondes respectivement).
 strategy peut être définie entre, performance et freshness.
Performance: le service worker va répondre en priorité avec les données en
cache.
freshness le service worker va d’abord effectuer une requête réseau et ne
répondra avec le cache que si cette requête est en timeout.
timeout permet de spécifier la durée avant de considérer que le réseau est en
timeout. Le service worker Angular va attendre ce temps avant d’utiliser la réponse
en cache (si il est configuré en stratégie freshness comme nous allons le voir).
898
Exercice
 Ajouter dans votre composant l’affichage de la liste des users
récupérées depuis [Link]
 Faite en sorte que vos appels d’apis soient cachées.
 Rebuilder votre application et tester son fonctionnement en offline.

899
Les services workers
Résumé
 Pour résumer, voici comment les nouvelles versions d'application sont
gérées par Angular Service Worker :
 Pour chaque nouvelle version, un nouveau [Link] est
disponible
 Pour le premier rechargement de l'application après le déploiement
de la nouvelle version - Angular Service Worker détecte le nouveau
[Link] et charge tous les nouveaux fichiers en arrière-plan
 Pour le deuxième rechargement après le déploiement de la nouvelle
version - l'utilisateur voit la nouvelle version

900
Les Push Notifications
 Les push notifications web sont des informations que vous pouvez
envoyer à vos utilisateurs s’ils ont accepté d’en recevoir, et ce
même si votre site est fermé.
 Pour recevoir une notification il suffit que leur navigateur soit
ouvert, même en tâche de fond.
 Vous pouvez penser aux notifications push sur mobile.
 Afin de faire ca, vous avez besoin d’un script qui fonctionne en
tache de fond. C’est le Service Worker

901
Les Push Notifications
 Les push notifications web sont une combinaison de Web Push et
de notification.
 L’API de notification du browser vous permet d’afficher des
notifications à votre utilisateur. Ceci est indépendant de l’API Web
Push.
if ('Notification' in window) {
[Link](`Vous posséder l'API de Notification`);
if ([Link] === 'granted') {
[Link]('Permission accordée');
new [Link]('Ceci st ma première notification!');
}
} 902
Les Push Notifications
 Le push API permet aux applications Web de recevoir des
messages d'un serveur, même lorsque le site Web n'est pas ouvert.
 Nous pouvons utiliser cette API pour recevoir des messages puis les
afficher, en utilisant l'API de notification.
 Cette API est uniquement disponible via les service workers, qui
permettent d'exécuter du code en arrière-plan lorsque l'application n'est
pas ouverte.

903
Les Push Notifications
 Cependant, les navigateurs ne nous permettent pas d’envoyer les
notifications de notre serveur directement au navigateur de
l'utilisateur.
 Ceci est du à la peur de spammer l’utilisateur avec des notifications.
 Au lieu de cela, seuls certains serveurs spécifique au navigateur
pourront envoyer des notifications à un navigateur donné.
 Ces serveurs sont connus sous le nom de Browser Push Service.
Notez que par exemple, le service Push utilisé par Chrome (Firebase
Cloud Messaging) est différent de celui utilisé par Firefox (autopush).

904
Les Push Notifications

905
Les Push Notifications
 Afin de pouvoir délivrer un message à un utilisateur donné et
uniquement à cet utilisateur, le Service Push identifie l'utilisateur
de manière anonyme, garantissant la confidentialité de l'utilisateur.
 Afin que le Service Push puisse analyser le comportement du serveur,
il doit pouvoir l’identifier.
Afin de faire ca on va utiliser le protocole VAPID (Voluntary
Application Server Identification)

906
Les Push Notifications
VAPID
 Une paire de clés VAPID est une paire de clés publique/privée
cryptographique utilisée de la manière suivante :
 La clé publique est utilisée comme identifiant unique du serveur pour
abonner l'utilisateur aux notifications envoyées par ce serveur
 La clé privée doit être gardée secrète et utilisée par le serveur d'application
pour signer les messages, avant de les envoyer au service Push pour livraison.
 Afin de générer les clés, installer la librairie web-push côté serveur
npm install -g web-push
 Pour générer la paire de clés nécessaire au protocole VAPID :
web-push generate-vapid-keys --json
907
Les Push Notifications
VAPID
 Pour notre serveur de notification, voici les deux clés utilisés :
vapidKeys = {
publicKey:
'BCucKI9WG2aEbOhigJ6y_3Z28GIe0jp9QjaZtXXUABjGoUMup6IoYUrTSM
h72MxP3t6eVoV6cZ1doEGMm9cRJ2Q',
privateKey: 'OQazMChcws-F21vMTt81rlpRr4eZsWkR9yQlZAf0lW0',
};

908
[Link] 909
Les Push Notifications

910
Les Push Notifications
1- Inscription au service push
 Côté client : autorisation et PushSubscription
 La première étape est d’inscrire un utilisateur pour qu’il puisse
recevoir des notifications de votre application.
 Il faut d’abord obtenir son autorisation avec une pop-up spécifique,
native au navigateur.
 Ensuite, il est nécessaire d’obtenir du navigateur une
PushSubscription.
 Cette PushSubscription contient toutes les informations nécessaires
pour pouvoir envoyer une notification à un utilisateur donné.
 Cette inscription utilise l’API push.
911
Les Push Notifications
Quelles sont les étapes pour permettre
l’activation des notifications web push ?
 Afin de recevoir cette PushSubscription, injecter le service SwPush offert
par votre ServiceWorkerModule.
 Utilisez ensuite sa méthode requestSubscription qui prend en paramètre
un objet d’option dont le paramètre obligatoire est la propriété
serverPublicKey qui correspondra à la clé publique de votre VAPID.
 Lorsque vous l’appelez, cette méthode va déclencher un popup demandant
à l’utilisateur s’il accepte ou pas d’être notifié (ceci se fait une seule fois).
 S’il accepte la fonction retourne une promesse qui va émettre la
PushSubscription.
 S’il n’accepte pas vous n’aurez aucun moyen de lui redemander.

912
Les Push Notifications
Quelles sont les étapes pour permettre
l’activation des notifications web push ?
[Link]
.requestSubscription({
serverPublicKey: [Link],
})
.then((sub:PushSubscription) => {
// Faites ce que vous voulez avec cet objet
})
.catch((err) => {//en cas de refus}
);

913
Les Push Notifications
Quelles sont les étapes pour permettre
l’activation des notifications web push ?
 L’objet Push Subscription contient les informations suivantes :

{
subscription: {
endpoint: '[Link]
expirationTime: null,
keys: {
p256dh: 'BJIRfMw6Va3GG2u…',
auth: 'xBnPrD-I0_xNzikHRuxCxg'
}
}
}

914
Les Push Notifications

915
Exercice
 Mettez en place le système de push notification afin de récupérer
l’objet de type pushSubscription
 Vérifier ce qui se passe lorsque vous dites non lors de la demande
d’autorisation.
 Afin d’annuler les demandes d’autorisation que ce soit l’accpetation et
les rejets vous pouvez suivre ce lien
chrome://settings/content/notifications

916
Les Push Notifications
Quelles sont les étapes pour permettre
l’activation des notifications web push ?
 2 - Le client envoi une demande d’inscription au serveur en y incluant
l’objet subscription.
 3 - Côté serveur : Le serveur doit sauvegarder le subscription
 Votre serveur reçoit la PushSubscription envoyée par votre
application cliente et l’enregistre habituellement dans une base de
données pour pouvoir envoyer des notifications à l’utilisateur.
 Pour envoyer une notification, vous allez devoir passer par un service
push qui va ensuite l’envoyer à l’utilisateur, et ce à des fins de contrôles.
 Si le navigateur n’est pas en ligne, il va la mettre en attente
jusqu’à ce que l’utilisateur l’ouvre.
917
Les Push Notifications
Quelles sont les étapes pour permettre
l’activation des notifications web push ?
subscribeToNotifications() {
[Link]
.requestSubscription({
serverPublicKey: [Link],
})
.then((sub:PushSubscription) => {
[Link]({sub});
[Link]
.post('[Link] sub)
.subscribe((data) => [Link]({ data: data }));
})
.catch((err) =>
[Link]('Could not subscribe to notifications', err)
);
}

918
private sendPushNotification(subscription) {
webpush
.sendNotification(
subscription,
[Link]({
notification: {
title: 'Our first push notification',
body: 'Here you can add some text',
},
}),
options,
)
.then((log) => {
[Link]('Push notification sent.');
[Link](log);
})
.catch((error) => {
[Link](error);
});
}
}

[Link] 919
Exercice
 Connecter vous au service de notification suivant et tester la réception
d’une notification.
[Link]

920
Server Side Rendering
Angular Universal
 Angular Universal est une librairie développée par l'équipe d'Angular
qui permet le Server Side Rendering (SSR) et le prerendring de
votre application.
 Dans son fonctionnement standard, une application Angular va
s'exécuter dans le navigateur et ne va entamer l’affichage qu’après s'être
chargée.
 Afin d’éviter cet aspect ‘page blanche’ et éviter à l’utilisateur
d’attendre de peur qu’il quitte la page, Angular Universal va permettre
de générer des pages statiques sur votre serveur afin de rendre votre
application accessible avant que votre application SPA ne soit
chargée.
921
Server Side Rendering
Angular Universal
Comment ca marche ?
 Lorsque nous utilisons Angular Universal, nous restituons à
l'avance le HTML et le CSS initiaux présentés à l'utilisateur.
 Nous pouvons le faire par exemple au moment de la construction,
ou à la volée sur le serveur lorsque l'utilisateur demande la page.
 Ce code HTML et CSS sera d'abord servi à l'utilisateur, afin que
l'utilisateur puisse voir rapidement quelque chose à l'écran.
 En même temps, votre application Angular sera chargé côté
client et prendra alors le contrôle de la page, et à partir de là, tout
fonctionnera comme une SPA normale

922
Server Side Rendering
Angular Universal
Pourquoi ?
 Il existe trois raisons principales pour utiliser Angular Universal :
1. Affichez la première page rapidement avec un premier rendu
(FCP)
2. Améliorer les performances sur les appareils mobiles et à faible
puissance.
3. Faciliter le travail des robots d'exploration (crawlers) des réseaux
sociaux et d'exploration Web pour l'optimisation des moteurs de
recherche (SEO).

[Link] 923
Server Side Rendering
Angular Universal
Mise en place
 Afin de mettre en place Angular
Universal, Angular nous offre un
Schematic prêt à l’emploi. Lancez la
commande suivante :
ng add @nguniversal/express-
engine
 La commande met à jour le code de
l'application pour activer le SSR et
ajoute des fichiers supplémentaires à
la structure du projet.

[Link] 924
Exercice
 Télécharger le projet de ce répo Github :
[Link]
 Mettez en place Angular Universal

925
Server Side Rendering
Angular Universal
[Link]
 C’est le fichier d’entrée de la partie serveur.
 Il exporte le AppServerModule qui lui représente le AppModule du
serveur.

926
Server Side Rendering
Angular Universal
[Link]
 Ce fichier représente le serveur ts qui permettra de gérer les requêtes.
 ngExpressEngine permet de rendre l'application avec Express et de
gérer la mise en cache des requêtes.
// Example Express Rest API endpoints
// [Link]('/api/**', (req, res) => { }); [Link]('html', ngExpressEngine({
// Serve static files from /browser bootstrap: AppServerModule
[Link]('*.*', [Link](distFolder, { }));
maxAge: '1y'
}));

// All regular routes use the Universal engine


[Link]('*', (req, res) => {
[Link](indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: [Link] }] });
});
927
Server Side Rendering
Angular Universal
Lancer votre serveur en mode ssr
 Dans le fichier [Link], vous avez plusieurs commandes ajoutés
par Angular Universal
 dev:ssr, permet de servir la version SSR de l'application pendant le
développement pour rebuilder à chaque changement (comme ng serve).
 serve:ssr, permet de lancer la version buildé en test, elle sert pour tester
localement l'application rendue côté serveur
 build:ssr, Génère à la fois le script du serveur et l'application en mode
production. Utilisez cette commande lorsque vous souhaitez générer le
projet pour le déploiement "scripts": {
"dev:ssr": "ng run ngUniversalStarting:serve-ssr",
"serve:ssr": "node dist/ngUniversalStarting/server/[Link]",
"build:ssr": "ng build && ng run ngUniversalStarting:server",
"prerender": "ng run ngUniversalStarting:prerender",
}, 928
Exercice
 Mettez en place Angular Universal
 Tester le fonctionnement de votre application avec ng serve et npm
run dev:ssr, vérifier le code source de votre application
Certaines versions d'Angular ne permettent pas la vérification du
résultat SSR dans votre navigateur. Malgré que le ssr fonctionne côté
serveur.
 Afin de vérifier ca, utilisez curl en tapant pour notre cas :
curl [Link] > [Link]

929
Exercice
 Dans votre AppComponent, ajouter une phrase affichant le nombre d’accès
à votre application et qui devra s’incrémenter à chaque chargement de votre
application.
 Commencez par tester votre application en mode standard.
 Lancez ensuite votre application en mode SSR.
 Que remarquez vous et quelle est votre interprétation ?
 Certaines versions d'Angular ne permettent pas la vérification du
résultat SSR dans votre navigateur. Malgré que le ssr fonctionne côté
serveur.
 Afin de vérifier ca, utilisez curl en tapant pour notre cas :
curl [Link] > [Link]

930
Server Side Rendering
Angular Universal
Les API du Browser
 Etant donnée que vous utilisez un serveur afin de lancer votre
application, il ne peut pas accéder aux API du Navigateur, telles que
Window, Location ou localstorage.
 Ceci implique qu’il ne faut pas utiliser ces objets pour le rendu
serveur.
 Afin de gérer ce problème, il faudra injecter le token PLATFORM_ID
pour savoir si vous êtes dans un rendu serveur ou navigateur;
 Il existe deux helpers qui prenne en paramètre le platform_id pour
identifier votre contexte :
 isPlatformBrowser
 isPlatformServer
931
Server Side Rendering
Angular Universal
Les API du Browser
import { isPlatformBrowser, isPlatformServer } from '@angular/common';
import { Inject, PLATFORM_ID } from '@angular/core';
constructor(
@Inject(PLATFORM_ID) private platformId: Object
) {}

ngOnInit() {
if (isPlatformBrowser([Link])) {
// code navigateur navigateur
}
if (isPlatformServer([Link])) {
// code serveur
}
}

[Link] 932
Exercice
 Faite le nécessaire pour résoudre le problème avec le localstorage

933
Server Side Rendering
Angular Universal
Les appels doubles des API (Serveur / Client)
 Lorsque vous utilisez Angular Universal pour le chargement de votre
page, si cette dernière nécessite des données d’une API, un premier
appel sera fait au niveau serveur.
 Ensuite, lorsque l’application Angular sera chargé, un second appel
sera effectué. Nous identifions ici deux problème :
 Si vous avez une connexion un peu lente (3G pour mobile par
exemple), l’utilisateur remarquera un aspect de flickering. L’interface
se rechargera avec la liste récupérée du second appel.
 Le double appel de l’API.

[Link] 934
Server Side Rendering
Angular Universal
Le TransferHttpCacheModule
 La première solution que vous pouvez utiliser est le
TransferHttpCacheModule.
 TransferHttpCacheModule est un module utilisé dans les applications
Angular pour éviter les requêtes HTTP GET dupliquées côté client.
 Il installe un intercepteur HTTP qui intercepte les requêtes HTTP
GET effectuées par le HttpClient côté client.
 L'intercepteur. vérifie si la même demande a déjà été faite
lorsque l'application a été rendue côté serveur
 Si la demande a déjà été faite, l'intercepteur extrait la réponse du store
clé-valeur TransferState.
[Link] 935
Server Side Rendering
Angular Universal
Le TransferHttpCacheModule
 La réponse est ensuite transférée côté client, où elle est utilisée pour répondre
aux mêmes requêtes HttpClient côté client.
 Cela évite les requêtes HTTP GET dupliquées côté client, ce qui peut
améliorer les performances de l'application.
 Il est important de noter que cela ne fonctionne que pour les requêtes HTTP
GET et non pour les autres types de requêtes.
 Pour utiliser le module TransferHttpCacheModule, vous devez l’importer dans
votre AppModule. import { TransferHttpCacheModule } from "@nguniversal/common";
@NgModule({ imports: [
BrowserModule,
TransferHttpCacheModule,
],})
export class AppModule {}
[Link] 936
Server Side Rendering
Angular Universal
L’API TransferState
 La deuxième méthode pour gérer ce problème, est de manuellement
utiliser l'API TransferState.
 Ceci peut être utile pour gérer l’incapacité du
TransferHttpCacheModule de gérer les méthodes non POST ou de
personnaliser le comportement selon votre besoin
 L’API nous fournit un conteneur de stockage pour transférer
facilement des données entre le serveur et l'application cliente,
évitant ainsi à l'application cliente d'avoir à contacter le serveur pour
obtenir les données.

[Link] 937
Server Side Rendering
Angular Universal
L’API TransferState
 Dans l’application cliente et afin de l’utiliser, injecter le service
TransferState de @angular/core.
 Sur le serveur, il est déjà inclus si la fonction renderApplication est
utilisée. Sinon, importez le module ServerTransferStateModule
pour rendre le TransferState disponible.
 Les valeurs dans le store sont sérialisées/désérialisées à l'aide de
[Link]/[Link].
 Seuls les booléens, numériques, chaîne, nuls et les objets non classe
seront sérialisés et désérialisés sans perte. const key = makeStateKey<any>("cvs");
 La clé utilisée afin de gérer les données doit être de type StateKey
[Link] 938
Exercice
 Utilisez l’objet TransferState afin de gérer le probléme de double
chargement de l’api de la liste des Cvs.

939
Server Side Rendering
Angular Universal
Prerender
 Nous avons vu comment Angular Universal utiliser le serveur afin de
pré charger une route à la volée.
 Ce n’est pas l’unique solution, Angular Universal nous permet de pré
charger une page, non à la volée, mais lors du Build. C’est le
Prerender.
 Cela permet d'envoyer directement les pages rendus sans avoir à
effectuer aucun traitement (Comme le ferai un serveur standard pour
une page HTML).
 Ceci permet d’être le plus performant possible. Cependant, les pages
générées restent statiques et non dynamiques.
940
Server Side Rendering
Angular Universal
Prerender
 Afin de lancer ce mécanisme de prerender, Angular Universal nous a
préparé la commande pour.
 Fermer vos serveur et lancer la commande npm run prerender
"scripts": {
"dev:ssr": "ng run ngUniversalStarting:serve-ssr",
"serve:ssr": "node dist/ngUniversalStarting/server/[Link]",
"build:ssr": "ng build && ng run ngUniversalStarting:server",
"prerender": "ng run ngUniversalStarting:prerender",
},

941
Server Side Rendering
Angular Universal
Prerender - option
 La fonction prerender permet de charger les routes statiques.
 Afin de générer les routes dynamiques ajouter l’option --routes suivi
de la route souhaitée.
"prerender": "ng run ngUniversalStarting:prerender --routes 'cv/1' --routes 'cv/2' ",

 Vous pouvez également utiliser un fichier au même niveau que


[Link]
"prerender": "ng run ngUniversalStarting:prerender --routes-file [Link] ",

 Vous pouvez aussi lui spécifier quelles routes vous voulez prérendrer
"prerender": "ng run ngUniversalStarting:prerender --no-guess-routes --routes /home /cv",

[Link] 942
Angular Material
 Angular Material est une librairie permettant d'intégrer et de
personnaliser des composants Angular en suivant le Material Design.
 Material Design est un système de conception conçu et pris en charge
par les concepteurs et les développeurs de Google.
 [Link] inclut des conseils UX approfondis et des implémentations
de composants d'interface utilisateur pour Android, Flutter et le Web.
 Afin d’installer Angular Material lancer la commande :
ng add @angular/material
 Vous aurez à choisir le thème que vous voulez utiliser dans votre
projet puis à spécifiez si vous souhaitez inclure les animations.
[Link] 943
Angular Material
Les éléments du Formulaire
Le composant <mat-form-field>
 Pour fonctionner, plusieurs composants d’Angular material ont
besoin du composant <mat-form-field> qui est utilisé comme
wrapper.
 Les composants Material concernés sont :
les input
les select
Pour l’utiliser il suffit de l’importer depuis la librairie Material :
import {MatFormFieldModule} from '@angular/material/form-field';

[Link] 944
Angular Material
Les éléments du Formulaire
 Voici quelques-uns des attributs
importants de `mat-form-field` :
appearance : Cet attribut contrôle
l'apparence du champ de formulaire.
Les valeurs possibles sont "fill" par
défaut et "outline".
 floatLabel : Cet attribut contrôle le
comportement de l'étiquette du
champ lorsqu'il est en focus ou
contient une valeur. Les valeurs
possibles sont "auto", "always" et
"never".
[Link] 945
Angular Material
Les éléments du Formulaire
 <mat-hint> : Vous pouvez ajouter un texte d'indication (hint) pour
guider les utilisateurs lorsqu'ils remplissent le champ.
 directive matTextPrefix et matTextSuffix : Vous pouvez ajouter un
préfixe/suffixe (par exemple, une icône) au champ de formulaire.
<mat-form-field floatLabel="auto">
<mat-label>You favorite food</mat-label>
imports:
<mat-hint>Une petite astuce</mat-hint>
[
<span matTextPrefix>
MatFormFieldModule,
<mat-icon> restaurant </mat-icon>
MatInputModule,
</span>
MatIconModule,
<input matInput />
];
</mat-form-field>

[Link] 946
Angular Material
Les éléments du Formulaire
Les Inputs
 matInput est une directive qui permet aux éléments natifs <input>
et <textarea> de fonctionner avec <mat-form-field>.
 Vous pouvez spécifiez les erreurs avec la balise mat-error
 Le champ posséde plusieurs attributs:
 placehoder
 value
…

[Link] 947
Angular Material
Les éléments du Formulaire
Les checkbox
 Pour utiliser le module Material pour les checkbox, il suffit de
l’importer puis d’appeler la balise <mat-checkbox> </mat-checkbox>.
 import { MatCheckboxModule } from '@angular/material/checkbox';
 La checkbox de Material supporte toutes les fonctionnalités d’HTML
5 et a la même API, e.g. checked, labelPosition, name

<mat-checkbox labelPosition="before">Sélectionne moi </mat-checkbox>

<mat-checkbox checked color="primary">Je suis séléctionné </mat-checkbox>

[Link] 948
Angular Material
Les éléments du Formulaire
Les select Listes <mat-select>
 Afin de gérer les lists,
commencer par importer le
MatSelectModule
import { MatSelectModule }
from '@angular/material/select'; <mat-form-field>
<mat-label>Frameworks</mat-label>
 mat-select doit vivre au sein <mat-select>
<mat-option value="angular">Angular</mat-option>
d’un mat-form-field <mat-option value="nest">Nest</mat-option>
<mat-option value="symfony">Symfony</mat-option>
 Pour identifier les options de </mat-select>
votre liste utiliser <mat-option> </mat-form-field>
[Link] 949
Angular Material
Les éléments du Formulaire
 Vous pouvez avoir un select multiple en ajoutant la propriété
multiple dans le mat-select.

<mat-form-field>
<mat-label>Frameworks</mat-label>
<mat-select multiple>
<mat-option value="angular">Angular</mat-option>
<mat-option value="nest">Nest</mat-option>
<mat-option value="symfony">Symfony</mat-option>
</mat-select>
</mat-form-field>

[Link] 950
Angular Material
Les boutons
 Afin d’utiliser les boutons de Material, importer le MatButtonModule
import { MatButtonModule } from "@angular/material/button";
 Material Propose plusieurs variantes de boutons
 La propriété color permet de spécifier
La couleur
 le bouton par défaut est fourni avec la
Directive mat-button

[Link] 951
Angular Material
Les boutons
<button
mat-raised-button
color="primary“
>
raised primary
</button>

[Link] 952
Angular Material
Les menus Material
 Ce sont des panneaux flottants contenant une liste d’options fournies
par MatMenuModule
import {MatMenuModule} from '@angular/material/menu';
1. Créer d’abord un mat-menu comprenant plusieurs options qui sont
des boutons sur lesquels est ajouté l’attribut mat-menu-item
2. Récupérer une référence locale (via #) sur le menu auquel vous
assignez la directive matMenu.
<mat-menu #monMatMenu="matMenu">
<button mat-menu-item>Option 1</button>
<button mat-menu-item>Option 2</button>
</mat-menu>

[Link] 953
Angular Material
Les menus Material
 Ce menu restera caché. Il faudra donc un élément pour l’ouvrir
comme un un bouton.
 Sur cet élément, nous bindons la directive matMenuTriggerFor
auquelle nous passons la référence du menu.
<mat-menu #monMatMenu="matMenu">
<button mat-menu-item>Option 1</button>
<button mat-menu-item>Option 2</button>
</mat-menu>
<p [matMenuTriggerFor]="monMatMenu">Menu ouvre toi</p>

[Link] 954
Angular
Internationalisation
ngx translate
AYMEN SELLAOUTI

955
Ngx-Translate
 Ngx-Translate est une bibliothèque de traduction pour angular.

Vous permet de définir la traduction de votre contenu en plusieurs langues et de


switcher entre eux facilement.

[Link]

956
Ngx-Translate
Installation
 Afin d’installer ngx-translate vous devez d’abord installer le core de cette
bibliothèque.

npm install @ngx-translate/core


Une fois le core installé, il vous faut un loader qui vous permet de récupérer les
fichiers de traductions.

@ngx-translate/http-loader

[Link] 957
Ngx-Translate
Installation
 Initialiser le module de traduction, TranslationModule
 Le HttpLoaderFactory est requis pour la compilation AOT de votre projet.

[Link]({  L’instanciation du
defaultLanguage: 'fr',
loader: {
TranslateHttpLoader prend en
provide: TranslateLoader, paramètre le service
useFactory: HttpLoaderFactory, permettant de récupérer les
deps: [HttpClient],
},
fichiers de traduction, le
}), chemin vers les fichiers de
traduction et leur extension.
// AoT requires an exported function for factories  Par défaut les valeurs sont :
export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http); './assets/i18n/', '.json'
} 958
Ngx-Translate
Utilisation
 Dans le composant qui va se charger de la traduction et du changement de la
langue, injecter votre service.
constructor(public translateService: TranslateService) {
}

 Ce service offre plusieurs fonctions vous permettant de gérer la langue.


 setDefaultLang qui prend en paramètre la langue par défaut.
 addLang qui prend en paramètre un tableau de langue de votre application.
 getBrowserLang qui retourne la liste des langues définies.
 use qui prend en paramètre la langue que vous voulez instaurer dans le site.

[Link] 959
Ngx-Translate
Utilisation
 Ce service offre plusieurs fonctions vous permettant de gérer la langue.
 setDefaultLang qui prend en paramètre la langue par défaut.
 addLang qui prend en paramètre un tableau de langue de votre application.
 getBrowserLang qui retourne la liste des langues définies.
 use qui prend en paramètre la langue que vous voulez instaurer dans le site.

constructor(public translateService: TranslateService) {


[Link]('fr');
[Link](['en', 'fr']);
const browserLang = [Link]();
[Link]([Link](/en|fr/) ? browserLang : 'fr');
}

[Link] 960
Ngx-Translate
Les fichiers de traductions
 Les fichiers de traduction sont par défaut sous le dossier ‘assets/i18n/.
 Ils contiennent simplement un objet JSON de paires clé-valeur, où la clé décrit le
texte qui est traduit et la valeur est le texte réel dans la langue spécifiée par le fichier.
 La valeur peut également être un autre objet, ce qui vous permet de regrouper vos
traductions comme vous le souhaitez.
Dans le texte de votre valeur de traduction, vous pouvez également inclure des
doubles accolades autour d'un nom de variable, ce qui vous permettra plus tard
d'interpoler des chaînes de manière dynamique dans vos traductions.

[Link] 961
Ngx-Translate
Les fichiers de traductions
{ {
"[Link]": "Translation Example", "[Link]": "Exemple de traduction",
"HOME" : { "HOME": {
"welcomeMessage": "Thanks for joining, {{ fir "welcomeMessage": "Nous vous remercions pour votre prés
stName }}! It's great to have you!", ence, {{ firstName }}! C'est un plaisir de vous avoir avec nous!",
"login": { "login": {
"username": "Enter your user name", "username": "Veuillez saisir votre nom d'utilisateur",
"password": "Password here" "password": "Votre mot de passe"
} }
} }
} }
[Link]
[Link]
 Les parties variables de vos messages doivent être interpolé et passé en paramètres
lorsque vous aller utiliser la traduction.

[Link] 962
Utiliser la traduction
 Maintenant que tout est en place, il faudra voir comment utiliser cette traduction.
 Afin de spécifier les éléments traductibles, vous avez deux méthodes. Les pipes, et
les directives.
<h1 [translate]="'[Link]'"></h1>
<h2 translate="">[Link]</h2>
<input type="text" placeholder="{{ '[Link]' | translate }}" />
 Voici comment passer des paramètres avec les deux méthodes.

<h2 translate [translateParams]="{ firstname: 'aymen' }">[Link]</h2>


<h2 [translate]="'[Link]'" [translateParams]="{ firstname: 'aymen' }"></h2>
<h2>{{ "[Link]" | translate: { firstname: 'aymen' } }}</h2>

[Link] 963
Exercice
 Traduisez les pages des CVs

964
Angular internationalization i18n
 La team Angular a choisi d’implémenter un système
d’internationalisation au moment du Build.
 Au lieu de faire une traduction au runtime, votre projet
sera buildé en plusieurs langues. Vous aurez donc un build
par langue.
 Pour implémenter ce mécanisme, commencer par ajouter le
package @angular/localize.
ng add @angular/localize

[Link] 965
Angular internationalization i18n
Spécifier les langues de traductions
 Une fois le package installé, nous devons mettre en place notre
système pour l’internationalisation
1. La première étape consiste à modifier le fichier [Link] afin de
spécifier quelles locales seront supportées. Aller sur projects ->
nomVotreApp et ajouter une clé i18n.
Cette clé prend 2 paramètres:
 sourceLocale: la locale par défaut
 locales : objet des autres locales avec
comme clé la locale et comme valeur un objet d’options ou le chemin du
fichier de traduction
966
Angular internationalization i18n
Spécifier les langues de traductions
 Pour la partie serve et étant donner que vous ne pouvez servir qu’une seule
langue vous devez définir les configuration des langues une par une.
 Allez dans la partie [Link] et ajouter la configuration de la langue.
 Ensuite, dans la partie [Link] définissez un script et passez lui la clé de
la langue que vous avez définie
 Maintenant pour lancer votre serveur dans la langue souhaitée ajouter
ng serve –configuration=votreLocale
"build": { "serve": {
"configurations": { "builder": "@angular-devkit/build-angular:dev-server",
"en": { "configurations": {
"localize": ["en-US"] "en": {
}, "browserTarget": "materialExamples:build:development,en"
}
[Link] 967
Angular internationalization i18n
Spécifier les langues de traductions
 Afin de builder votre application avec les différentes locales vous
allez dans build->options et mettez la propriété localize à true ce qui
vous oblige à générer la configuration serve pour toutes les langues.
 Sinon enlevez le localize à true et lancez ng build --localize

"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"localize": true,

968
Angular internationalization i18n
Spécifier les éléments à traduire
2. La deuxième étape est de définir quels éléments traduire
 Utiliser l’attribute i18n afin de spécifier qu’un élément textuel du
template doit être traduit.
 L'attribut i18n prend une chaîne de caractères optionnelles qui va
permettre de paramétrer la traduction du texte.
Cette chaîne est un contexte comportant une signification et un
descriptif pour aider le traducteur. signification | descriptif@@Id.
La signification permet de ne traduire la même chose qu’une seule fois.
<mat-hint i18n="hint | message pour spécifier une astuce">Une petite astuce</mat-hint>

969
Angular internationalization i18n
Génération des fichiers de traduction
3. La troisième étape est la génération des fichiers de traduction
 Pour extraire les fichiers de langue source, ouvrez une fenêtre de
terminal, accédez au répertoire racine de votre projet d'application et
exécutez la commande CLI suivante : ng extract-i18n
 La commande xi18n crée un fichier de langue source nommé
[Link] dans le répertoire racine de votre proje
 --output-path : modifiez l'emplacement.
 --format : modifier le format.
 --outFile : modifier le nom du fichier.

970
Angular internationalization i18n
Génération des fichiers de traduction
 Cette commande va générer un fichier [Link]
 Les élément à traduire sont dans des balises trans-unit
 La balise source représente l’élément à traduire
 les balises context décrivent le contexte de l’élément à traduire

<trans-unit id="4972876516686899215" datatype="html">


<source>la chaine Techwall</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/home/[Link]</context>
<context context-type="linenumber">7</context>
</context-group>
</trans-unit>

971
Angular internationalization i18n
Traduire les éléments
 Une fois le fichier généré, pour chaque langue, créer un nouveau fichier
avec le même contenu du fichier [Link]
 Par convention, il aura le nom [Link] => [Link]
 Ensuite, dans chaque trans-unit créer une balise target qui contiendra la
traduction de la source
<trans-unit id="4972876516686899215" datatype="html">
<source>la chaine Techwall</source>
<target>Techwall</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/home/[Link]</context>
<context context-type="linenumber">7</context>
</context-group>
</trans-unit>
972
Exercice
 Mettez en place le système de traduction pour avoir deux langues :
Français et Anglais
 Faite en sorte d’avoir deux scripts, chacun lançant la langue associée.
 Testez la trauction d’un premier composant

973
Angular internationalization i18n
Spécifier les éléments à traduire
2. La deuxième étape est de définir quels éléments traduire
 Afin de traduire un attribut, il faut préfixer le nom de l’attribut par i18n-
 Par exemple, pour traduire l’attribut title il faut taguer la balise avec
i18n-title à laquelle vous pouvez lui passez la même chaine que pour le
i18n.
<a
href="[Link]
title="Go To Google"
i18n-title
>google</a>

974
Exercice
 Ajoutez une image dans votre composant et traduisez les attributs alt
et title

975
Angular internationalization i18n
Spécifier les éléments à traduire
La traduction des pluriels
Les langues ont des constructions grammaticales très différentes. Par exemple 2
minutes ago se transforme en Il y a 2 minutes.
Souvent vous voudrez afficher différentes phrases suivant qu'il y ait zéro, une ou
plusieurs unités d'un objet considéré.
Pour cela, il suffit d'utiliser une variable contenant le nombre d'unités.
Par exemple, ici nous avons une variable minutes contenant le nombre de minutes
écoulées depuis la mise à jour d'un produit :
<span i18n>
Mis à jour {minutes, plural, =0 {maintenant} =1 {il y a une minute} other { il y a {{minutes}} minutes }}
</span>

[Link] 976
Angular internationalization i18n
Spécifier les éléments à traduire
La traduction des select
 Pour les selects, vous pouvez utilisez une syntaxe qui ressemble à celle des plural.
 Ouvrez les {, spécifiez votre propriété à traduire, ensuite le mot clé select ensuite
pour chaque élément, le nom puis {valeur}.

<span i18n>
Vous préférez un contrat {duration, select, long {long} short {short} indeterminate {indeterminate}}
</span>

[Link] 977
Angular internationalization i18n
Traduire les éléments
 Pour les plural et les select vous allez avoir une syntaxe ressemblante à
ce que vous avez défini dans la partie html

978
Angular internationalization i18n
Spécifier les éléments à traduire
La traduction des attributs côté TS
$localize() est une fonction globale d'Angular. Vous n'avez donc pas à
l'importer.
Elle permet de spécifier à Angular que vous voulez traduire un attribut
dans vos classes TS.
 title = $localize `Je suis Aymen`;
 i18n va en fait extraire le texte à traduire et le transformer en unité et
traduction et également va remplacer l'attribut par un gabarit étiqueté
avec la fonction $localize qui va permettre la traduction des chaînes de
caractères.
[Link] 979
Exercice
 Ajoutez une variable dans votre TS et traduisez la via $localize

980
Angular internationalization i18n
Génération des fichiers de traduction
 Lorsque un même élément est répété dans plusieurs parties le contexte
identifiera la totalité de ces éléments.
<trans-unit id="tecwallchanel" datatype="html">
<source>Le mur des technologies</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/home/[Link]</context>
<context context-type="linenumber">4</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/home/[Link]</context>
<context context-type="linenumber">8</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/home/[Link]</context>
<context context-type="linenumber">13</context>
</context-group>
</trans-unit> 981
Angular internationalization i18n
Configuration du serveur
 Voici un exemple simple avec express:
const express = require("express");
const path = require("path");
const app = express();
[Link]( [Link]([Link](__dirname, "../front/dist/front"), { index: false }));
[Link]((req, res, next) => {
if ([Link]("/en") || [Link]("/fr")) { next(); } else { [Link](`/en${[Link]}`);}
});
[Link]("/*", (req, res) => {
if ([Link]("/en")) {
[Link]([Link](__dirname, "../front/dist/front/en/[Link]"));
} else {
[Link]([Link](__dirname, "../front/dist/front/fr/[Link]"));
}
});
[Link](3000);
[Link] 982
Angular internationalization i18n
Configuration du serveur
 Le déploiement typique de plusieurs langues sert chaque langue à partir
d'un sous-répertoire différent (Les différents build par langage).
 Pour changer la langue, changez votre emplacement actuel vers un autre
sous-répertoire.
 Le changement de sous-répertoire s'effectue souvent à l'aide d'un menu
implémenté dans l'application.
 La configuration dépendra du serveur dans lequel vous déployez votre
application.

[Link] 983
9
8
4

Les directives structurelles


Comme déjà dit, les directives structurelles d'Angular sont les
directives qui manipulent les éléments du DOM, comme @if, @for et
@switch.
Avant la version 17, ces directives avaient une autre syntaxe avec
*ngIf, *ngFor et [ngSwitch].
Elles sont appliquées sur l’élément HOST (l'élément sur lequel la
directive est appliquée).
Angular 17
Le nouveau flux de control
Afin de continuer sur la logique de simplification de l’apprentissage
d’Angular, l’équipe Angular a proposé de nouveaux flux de
contrôle.
 Les flux suivants ont été proposés :
 @If
 @for
 @switch

985
Control flow
@if
@if est associé à une expression booléenne. Si cette expression est
fausse (false) alors l'élément et son contenu sont retirés du DOM, ou
jamais ajoutés).
@if(condition)
Si le booléen est true alors l’élément host est visible.
Si le booléen est false alors l’élément host est caché.
<button @if ([Link]()) {
*ngIf="[Link]()" <button
(click)="deleteCv(cv)" (click)="deleteCv(cv)"
class="btn btn-danger"> class="btn btn-danger">
Delete Delete
</button> </button>
}
986
Control flow
@if
Si vous utilisez l’ancienne version *ngIf avec des standalone
component, vous êtes dans l’obligation d’importer la directive.
@If est nativement reconnu par le compilateur, vous n’avez pas
besoin de l’importer.
@Component({
standalone: true,
template: `<div
*ngIf="condition">Content</div>`,
imports: [NgIf],
})
export class MyComponent {}

987
Control flow
@if, @else if et @else
@if peut également être utilisé avec @else if et/ou @else selon le
besoin.
La syntaxe est très intuitive, demandé vous comment vous aurez fait
en Javasript et ajoutez un @ :D.
<div *ngIf="connectedUser; else dissconnectedMessage"> @if (connectedUser) {
Hello {{ [Link]}} Hello {{ [Link]}}
</div> } @else {
<div>Merci de vous connectez</div>
<ng-template #dissconnectedMessage> }
<div>Merci de vous connectez</div>
</ng-template>

988
Control flow
@if, @else if et @else
@if (!connectedUser) {
<div class="alert alert-danger">Merci de vous connectez</div>
} @else if (![Link]) {
<div class="alert alert-warning">
Hello {{ [Link] }}, merci d'activer votre compte
</div>
} @else {
<div class="alert alert-success">Hello {{ [Link] }}</div>
}

989
9

Control flow
9
0

@For
La directive structurelle @for permet de boucler sur un itérable et
d'injecter les éléments dans le DOM.

<ul>
<li *ngFor="let episode of episodes">{{ [Link] }}</li>
</ul>
<ul>
@for (episode of episodes; track [Link]) {
<li>{{ [Link] }}</li>
}
</ul>
Control flow <ul>
@For <li
*ngFor="
let episode of episodes;
@for fournit certaines informations sur la let i = index;
let isOdd = odd;
boucle en cours : let isFirst = first
"
$index: position de l'élément. [ngClass]="{ odd: isOdd, bgfonce: isFirst }"
>
Episode {{ i + 1 }}{{ [Link] }}
$odd: true si l'élément est à une position impaire. </li>
</ul>
$even: true si l'élément est à une position paire. <ul>
@for (episode of episodes; track [Link]) {
$first: true si l'élément est à la première position. <li
[ngClass]="{ odd: $odd, bgfonce: $first }"
>
$last: true si l'élément est à la dernière position. Episode {{ $index + 1 }} : {{ [Link] }}
</li>
}
</ul>

991
Control flow 9
9

@For
2

track
Avec @For la fonction de tracking est devenue obligatoire
La fonction de suivi créée via l'instruction track est utilisée pour
permettre au mécanisme de détection des changements
d’Angular de savoir exactement quels éléments mettre à jour
dans le DOM après les modifications de l’itérable d'entrée.
La fonction de suivi indique à Angular comment identifier de
manière unique un élément de la liste.
@for (episode of episodes; track [Link]) {
<li>{{ [Link] }}</li>
}
Control flow 9
9

@For
3

track
En principe, il devrait toujours y avoir quelque chose d'unique dans
les éléments sur lesquels vous itérer.
Dans le pire des cas, s'il n'y a rien d'unique dans les éléments du
tableau, vous pouvez utiliser $index de l'élément, c'est-à-dire la
position de l'élément dans le tableau.

@for (episode of episodes; track $index) {


<li>{{ [Link] }}</li>
}
</ul>
Control flow 9
9

@For
4

track, pourquoi c’est devenu obligatoire ?


Dans son introduction de @for, Minko Gechev un des chefs de
projets Angular a argumenté le choix de rendre @For Obligatoire

We often see performance problems in apps due to the lack of


trackBy function in *ngFor. A few differences in @forare that
track is mandatory to ensure fast diffing performance. In
addition, it’s way easier to use since it’s just an expression
rather than a method in the component’s class.

[Link]
9

Control flow
9
5

@For, la gestion d’un itérable vide


Afin de gérer le cas ou votre itérable est vide, nous avons le block
@empty.
Ce bloque n’est activé que si l’itérable sur lequel vous bouclez est vide.
<ol class="list-group">
@for (player of players; track [Link]) {
<li class="list-group-item">{{[Link]}}</li>
}
@empty {
<li class="list-group-item list-group-item-danger">La liste ne nous est
pas encore parvenue</li>
}
</ol>
9

Control flow
9
6

@switch
<div [ngSwitch]="streamingService">
Avec @switch, vous pouvez créer <div *ngSwitchCase="'AppleTV'">Ted Lasso</div>
<div *ngSwitchCase="'Disney+'">Mandalorian</div>
des switch très simplement. <div *ngSwitchDefault>Peaky Blinders</div>
</div>
Vous avez les trois opérateurs : @switch(streamingService) {
@case ('Disney+') {
@switch, @case, @default <div>'Mandalorian'</div>
} @case ('AppleTV') {
@switch : définir l’élément sur <div>'Ted Lasso'</div>
} @default {
lequel switcher <div>'Peaky Blinders'</div>
}
@case : pour identifier le cas }

@default : pour les valeurs par @Component({


défaut standalone: true,
imports: [NgSwitch, NgSwitchCase, NgSwitchDefault],
})
export class SwitchComponent {
streamingService = 'Netflix';
}
9

Control flow
9
7

Migration automatique
Si vous voulez migrer votre ancien code et utilisez le nouveau work
flow, angular vous fournie une commande qui le fait pour vous :
ng g @angular/core:control-flow
Cette fonctionnalité est encore en developer Preview (Anguar 17.2)
9

Optimisation
9
8

@defer
 Dans cette partie nous allons voir les éléments suivants :
 Dans quels cas d’utilisation nous aurons besoin de defer
 Comment utiliser @defer
 Quels sont les blocks offerts avec @defer : @placeholder, @loading,
@error
 Qu’est ce qu’un trigger
 Quels sont les triggers offerts par Angular
 Comment créer son propre trigger
Optimisation 9
9

@defer
9

C’est quoi et à quoi ca sert ?


 L'une des méthodes les plus efficaces pour améliorer les performances d'une application Angular
consiste à différer autant que possible le chargement des ressources non critiques.
 Les ressources critiques doivent être chargées en premier, tandis que les autres ressources moins
cruciales doivent être chargées plus tard, uniquement si nécessaire. Ceci est fait à travers le lazy loading.
 Cependant, le lazy loading nous permet uniquement de différer un composant complet.
 Mais que se passe-t-il si vous disposez d'un grand écran avec de nombreuses dépendances et que vous
souhaitez charger des parties de la page qui ne sont pas immédiatement visibles au besoin.
 Par exemple quand l'utilisateur fasse défiler la page vers le bas ou clique sur un bouton.
 Ce type de chargement différé très fin n’est tout simplement pas possible avec le lazy loading.
 @defer permet un chargement différé d’une partie de votre composant
 Il permet de dire quand et pour quelle raison une partie sera chargée dans le navigateur de votre client.
1
0

Optimisation
0
0

@defer
 @defer est une syntaxe des templates Angular, qui vous permet de
charger des parties d'un modèle uniquement lorsqu'elles sont
nécessaires, compte tenu d'une condition logique.
 La syntaxe @defer nous permet d'implémenter des cas d'utilisation
courants tels que :
ne charge un composant volumineux qu'une fois que l'utilisateur a fait
défiler la page après un certain point
charger un composant volumineux uniquement après que l'utilisateur ait
cliqué sur un bouton
précharger un composant volumineux en arrière-plan pendant que
l'utilisateur lit la page, afin qu'il soit prêt au moment où l'utilisateur clique
sur un bouton
1
0

Optimisation
0
1

@defer
 @defer offre deux niveaux de control:
Quand pré charger le bundle de la partie à différer
Quand la section pré chargé sera affichée à l’utilisateur
 Pour que les dépendances au sein d'un bloc @defer soient différées, elles doivent
remplir deux conditions :
 Elles doivent être autonomes (standalones). Les dépendances non autonomes
ne peuvent pas être différées et seront toujours chargées au sein du bundle principal,
même à l'intérieur des blocs @defer.
 Ils ne doivent pas être directement référencés depuis le même fichier, en
dehors des blocs @defer ; cela inclut les requêtes ViewChild.
1
0

Optimisation
0
2

@defer
 Afin d’appliquer @defer sur un bloc, il suffit de
l’appeler dans votre template afin d’englober la <p>defer examples!</p>
partie concernée. @defer {
<app-huge/>
 Ceci permet la création d’un bundle spécifique }
pour toute cette partie
 Le nouveau bundle supplémentaire contenant le
HugeComposant ne sera chargé que lorsque le
bloc @defer sera déclenché.
 Ici, nous n'avons spécifié aucun déclencheur
pour le bloc @defer, il sera donc déclenché par
défaut lorsque le navigateur est inactif (idle).
 Le navigateur est considéré comme inactif
lorsque toutes les ressources de la page ont
fini de se charger.
Optimisation 1
0
0

@defer 3

@placeholder
 Parfois, nous souhaitons simplement afficher un espace vide à l’endroit où est placé notre bloc
@defer.
 Cependant, ceci peut avoir un comportement perturbant pour l’utilisateur, nous souhaitons donc
afficher du contenu initial à l'utilisateur, qui sera ensuite remplacé par le code chargé via le
bloc @defer.
 Nous pouvons le faire en encapsulant le contenu initial que nous souhaitons afficher dans un bloc
@placeholder.
 Chaque composant, directive ou pipe introduite dans le bloc @placeholder sera chargé dans le
bundle principal.
@defer {
<app-huge/>
}
@placeholder {
<h2>Nous chargeons le Huge Component merci de nous attendre :)</h2>
}
Optimisation 1
0
0

@defer 4

@placeholder
 Comme vous l’avez remarqué, et selon l’état de la connexion, l’utilisation du @placeholder peut
causer un effet de vacillement (flickering) qui est très désagréable pour l’utilisateur et provoque
donc une mauvaise expérience utilisateur.
 Afin de gérer ca, nous pouvons utiliser le paramètre minimum que nous pouvons passer à
@placeholder.
 Il permet de spécifier sa durée minimale d’affichage indépendamment du chargement du bloc
différé.

@defer {
<app-huge/>
}
@placeholder(minimum 1s) {
<h2>Je suis la en attendant le vrai component:)</h2>
}
Optimisation 1
0
0

@defer 5

@loading
 Le bloc @loading est utilisé pour afficher du contenu pendant que
<p>defer works!</p>
le bloc @defer charge toujours son bundle Javascript en arrière-
<p>defer examples!</p>
plan.
@defer {
 Le contenu du block @loading sera rendu uniquement pendant le <app-huge/>
chargement du bundle app-huge. Une fois le chargement }
terminé, le message sera supprimé de la page et le app-huge sera @placeholder(minimum 1s) {
affiché à sa place. <h2>Placeholder de AppHuge</h2>
}
 Le bloc @loading accepte deux paramètres facultatifs; minimum et @loading(minimum 1s; after 500ms){
after : <h3>AppHuge is loading.....</h3>
minimum est utilisé pour spécifier la durée minimale pendant }
laquelle le bloc @loading sera affiché à l'utilisateur.
after est utilisé pour spécifier le temps que nous devons
attendre avant d'afficher l'indicateur @loading après le démarrage
du processus de chargement.
Optimisation 1
0
0

@defer 6

@error
 Le chargement différé peut échouer pour une raison ou une autre ( problème réseau, bug, …)
 Afin de gérer ca vous pouvez utiliser le bloc @error
<p>defer works!</p>
<p>defer examples!</p>
@defer {
<app-huge/>
}
@placeholder(minimum 1s) {
<h2>Nous chargeons le Huge Component merci de nous attendre :)</h2>
}
@loading (minimum 1s; after 500ms) {
<h3>loading.....</h3>
}
@error {
<h2>Un problème est survenu, nous ne pouvons pas charger le Huge Component :'</h2>
}
Optimisation 1
0
0

@defer 7

Les déclencheurs
 Comme mentionné au début, @defer a deux niveaux de contrôle, chacun avec son déclencheur :
 le déclencheur facultatif de prefetch, qui contrôle le moment où le bundle est chargé depuis le
backend,
 le déclencheur facultatif @defer, qui contrôle le moment où le bloc @defer est affiché à
l'utilisateur.
 Rappelez-vous, ce sont deux événements très différents, et nous pouvons les contrôler séparément
et prendre en charge toutes sortes de cas d'utilisation avancés.
 Lorsqu'il s'agit de choisir le bon déclencheur, nous avons deux options disponibles :
 Utiliser des déclencheurs prédéfinis, qui couvrent tous les cas d'utilisation les plus courants
 Définir nos déclencheurs personnalisés, si nécessaire.
 Le mot-clé on est utilisé pour les déclencheurs prédéfinis, tandis que le mot clé when est utilisé pour
les déclencheurs personnalisés.
 idle est le déclencheur par défaut pour le chargement et l’affichage.
@defer(on idle ; prefetch on idle) {
<app-huge/>
}
Optimisation 1
0
0

@defer 8

Les déclencheurs
 Angular offre 6 déclencheurs:
 idle : déclenchera le chargement différé une fois que le navigateur aura atteint un état
d'inactivité (détecté à l'aide de l'API requestIdleCallback). C'est le comportement par défaut.
 viewport : déclenchera le bloc différé lorsque le contenu spécifié entre dans la fenêtre en
utilisant le IntersectionObservateur API. Il peut s'agir du contenu @placeholder ou d'une
référence d'élément.
 interaction : déclenchera le blocage différé lorsque l'utilisateur interagit avec l'élément
spécifié via des événements de clic ou de keydown.
 hover : déclenchera le blocage différé lorsque l'utilisateur va entrer dans l’élément spécifié,
les événements pris en considération ici sont mouseenter et focusin.
 immediate : déclenche immédiatement le chargement différé, ce qui signifie qu'une fois que le
client a terminé le rendu, le morceau (chunk) différé commencerait alors à être récupéré
immédiatement.
 timer : se déclencherait après une durée spécifiée. La durée est obligatoire et peut être spécifiée
en ms ou s.
Optimisation 1
0
0

@defer 9

viewport
 viewport : déclenchera le bloc différé @defer(on viewport) {
lorsque le contenu spécifié entre dans <app-huge/>
la fenêtre en utilisant le }
IntersectionObservateur API. Il peut @placeholder(minimum 1s) {
s'agir du contenu @placeholder ou d'une <h2>
référence d'élément. Je suis la en attendant le vrai component:)
</h2>
 Vous pouvez aussi spécifiez la référence }
d’un élément de votre template qui sera
le déclencheur lorsqu’on l’atteindra. <p #deferTriggerElement>defer examples!</p>
@defer(on viewport(deferTriggerElement)) {
<app-huge/>
}
@placeholder(minimum 1s) {
<h2>
Je suis la en attendant le vrai component:)
</h2>
}
Optimisation 1
0
1

@defer 0

interaction
interaction : déclenchera le blocage @defer(on interaction) {
différé lorsque l'utilisateur interagit <app-huge/>
avec l'élément spécifié via des }
événements de clic ou de keydown. @placeholder(minimum 1s) {
<h2>
 Vous pouvez aussi spécifiez la référence Je suis la en attendant le vrai component:)
d’un élément de votre template qui sera </h2>
le déclencheur lorsqu’on interagie }
avec.
<p #deferTriggerElement>defer trigger</p>
@defer(on interaction(deferTriggerElement)) {
<app-huge/>
}
@placeholder(minimum 1s) {
<h2>
Je suis la en attendant le vrai component:)
</h2>
}
Optimisation 1
0
1

@defer 1

hover
hover : déclenchera le blocage différé @defer(on hover) {
lorsque l'utilisateur va entrer dans <app-huge/>
l’élément spécifié, les événements pris }
en considération ici sont mouseenter @placeholder(minimum 1s) {
et focusin. <h2>
Je suis la en attendant le vrai component:)
 Vous pouvez aussi spécifiez la référence </h2>
d’un élément de votre template qui sera }
le déclencheur lorsqu’on y entre.
<p #deferTriggerElement>defer trigger</p>
@defer(on hover(deferTriggerElement)) {
<app-huge/>
}
@placeholder(minimum 1s) {
<h2>
Je suis la en attendant le vrai component:)
</h2>
}
Optimisation 1
0
1

@defer 2

Combiner des déclencheurs


 Plusieurs déclencheurs d'événements
peuvent être définis simultanément.
 Dans notre exemple, <p #deferTrigger>defer trigger</p>
@defer(on hover(deferTrigger); on timer(3s)) {
on hover(deferTrigger); on timer(3s) <app-huge/>
signifie que le bloc de report sera déclenché si }
l'utilisateur entre dans l'espace réservé, ou
après 3 secondes.
 Plusieurs déclencheurs on activés sont
toujours des conditions OU.
 De même, les conditions combinées avec
when sont également des conditions OU.
Optimisation 1
0
1

@defer 3

Créer vos trigger personnalisés


 Le principe d’un trigger personnalisé est simple. C’est une expression qui retourne un booléen.
 Quand l’expression devient vrai, le trigger est déclenché et le placeholder est remplacé
 Si la condition when redevient fausse, le bloc de placeholder n'est pas rétabli à l'espace réservé.

<input type="text" #myInput


(keyup)="[Link] > 4 ? loadDeffered = true : loadDeffered = false"
class="form-control«
>
@defer(when loadDeffered) {
<app-huge/>
}
@placeholder(minimum 1s) {
<h2>Je suis la en attendant le vrai component:)</h2>
}
Optimisation 1
0
1

@defer 4

Prefetching
 @defer vous permet de spécifier les conditions dans lesquelles le préchargement des chunks des
dépendances doit être déclenchée.
 Vous pouvez utiliser un mot-clé prefetch.
 La syntaxe de prefetch fonctionne de manière similaire aux principales conditions de defer et
accepte when et/ou on pour déclarer le déclencheur.
 Comme nous l’avons mentionné avant, ceci lorsqu’on associe when et on à defer, nous
contrôlons le rendu
 Lorsqu’on associe prefetch avec on et when, nous contrôlons quand récupérer les ressources.
 Cela permet des comportements plus avancés, tels que vous permettre de commencer à pré-
extraire des ressources avant qu'un utilisateur n'ait réellement vu ou interagi avec un bloc différé,
mais pourrait bientôt interagir avec lui, rendant les ressources disponibles plus rapidement.
Optimisation 1
0
1

@defer 5

Prefetching
<input type="text" #myInput (keyup)="[Link] > 4 ? loadDeffered
= true : loadDeffered = false" class="form-control">
@defer(on hover; prefetch when loadDeffered) {
<app-huge/>
}
@placeholder(minimum 1s) {
<h2>Je suis la en attendant le vrai component:)</h2>
}
@loading (minimum 1s; after 500ms) {
<h3>loading.....</h3>
}
[Link]@[Link]

1016

Common questions

Alimenté par l’IA

Best practices for writing actions in NgRx include initially writing actions before feature development to define functionality scope, categorizing actions by event source for clarity, and defining actions near the features they influence to maintain organization. Encouraging ample actions ensures clear expression of application flows. When grouped using ActionGroup, actions share a source context, aiding in maintainability and consistency across the application .

FormArray in Angular's Reactive Forms allows handling of dynamic form controls where the number of form controls is not predefined. Unlike FormGroup, which requires knowing controls in advance, FormArray lets developers dynamically add or remove FormControl instances in response to user interaction. Each FormControl is indexed numerically, and users can reference these controls using an appropriate getter method to perform operations like validations or updates .

NgRx Data abstracts over the NgRx store, effects, and entities, reducing the boilerplate required for state management. It provides default implementations based on conventions, freeing developers from manually creating actions, reducers, and effects for common CRUD operations. This simplicity comes at the cost of losing explicit control, requiring different handling for non-entity data, but significantly eases management of entity collections .

NavigationExtras in Angular provide a way to pass additional information during navigation. It is used as the second parameter in the router's navigate method, allowing dynamic data passage through the router. This method involves passing an object of type NavigationExtras, which contains properties like state whereby dynamic information, such as IDs, can be passed and later retrieved in the target component using the router's getCurrentNavigation() method .

NgRx Entity addresses the complexity and redundancy in managing state related to a collection of entities. It provides abstractions to standardize operations such as CRUD, significantly reducing boilerplate code. By utilizing a map structure for entities, it enhances efficiency in access and updates, contrasting with traditional array-based management which can be cumbersome for common operations like entity selection by ID .

Runtime checks in NgRx enforce rules on state and actions to prevent common errors, improving application stability. Enabled default checks include strict state and action immutability, ensuring that neither the state nor actions are directly mutated, thus maintaining data integrity and predictability. Optional configurations allow further checks like serialization, which monitors that state and actions are serializable, crucial for correct replay and debugging. These checks catch errors early, guiding adherence to best practices .

Angular handles changes in route parameters by using the Observable property params of the ActivatedRoute, which allows the component to dynamically react to parameter changes without recreating the component instance. When route parameters change, Angular does not call the constructor or ngOnInit again; instead, applications should subscribe to the params Observable to capture any parameter changes .

Using <a href> for routing in a Single Page Application (SPA) with Angular can lead to full page reloads, which are undesirable in SPAs because they disrupt the seamless user experience by reloading the entire page content. Instead, Angular provides the routerLink directive as a more appropriate method for routing within SPAs. The routerLink does not cause a full page reload and allows for dynamic content loading without leaving the currently rendered page .

Pre-loading selective modules in Angular can be achieved with a custom preloading strategy. Instead of using PreloadAllModules, which loads all modules at once, a custom strategy class implements the PreloadingStrategy interface where the preload method determines preloading based on route data. Developers can return a load Observable for wanted routes or null for others, allowing fine-grained control over which modules are preloaded based on application needs .

Smart Components, also known as container components, handle data fetching and complex operations, while Presentational Components, or pure components, focus on rendering data and UI elements without concern for their data source. Presentational components receive data through inputs and communicate user actions through outputs, promoting reusability and separation of concerns .

Vous aimerez peut-être aussi