Angular
Enseignant : Boudriga Imed
Boudriga Imed 1
PLAN
• Introduction
• Installation
• Création et structure d'un projet
• Affichage d'attribut (de type objet ou tableau) ou méthode
• Les Directives Angular
• Quelques Commandes utiles
• Les PIPES Angular
• Les Services Angular
• Les formulaires
• Les requêtes HTTP
• Routage
• sous-module
• interaction entre composant
• RXJS
Boudriga Imed 2
Boudriga Imed 3
INTRODUCTION
Angular est un Framework JavaScript, open source, présenté par Google en
2009;
Permettant de créer des applications web et mobiles
Front-End
Single page
utilisant
les composants web
l'injection de dépendance
le DOM Virtuel
le change détection.
Boudriga Imed 4
INTRODUCTION
Injection de dependance ?
• concept connu en programmation orientée objet
• utilisé par plusieurs frameworks Back-End (Spring, Symfony...)
• consistant a utiliser des classes sans faire de l'instanciation statique,
DOM Virtuel ?
• introduit par la librairie React
• une représentation en mémoire du DOM physique,
• les modifications se font sur ce DOM virtuel ensuite Angular s'occupe de
les synchroniser vers le DOM physique.
Boudriga Imed 5
INTRODUCTION
Change detection ?
• réalisé par la librairie « [Link] » (qu'on peut trouver dans node_module)
• utilisé pour synchroniser la vue avec le composant
• chaque composant dispose d'un change detector qui surveille en
particulier les expressions utilisées dans l'interpolation ou le propriété
binding.
• un changement de la valeur retournée par l'expression ⇒ mise à jour de la
vue.
Boudriga Imed 6
INTRODUCTION
Quelques outils utilisés par Angular
• npm (node package manager) : le gestionnaire de paquets par défaut pour
une application JavaScript,
• angular-cli command line interface : outil proposé par Google pour
faciliter la création et la construction d'une application Angular en
exécutant directement des commandes.
• webpack : bundler JavaScript
- construit le graphe de dépendances
- regroupe des ressources de même nature (.js ou .css...) dans un
ou plusieurs bundles
- fonctionne avec un systeme de module : un fichier JS est un `
module, un ficier CSS est un module...
• Ivy : le moteur de compilation et de rendu d'Angular integré
partiellement depuis la version 8 : intégralement dans la version 9 :
permettant d'accélérer la compilation et une meilleure lisibilité des messages
d'erreur.
Boudriga Imed 7
INTRODUCTION
Quels langages utilise Angular?
• HTML pour les vues
• CSS pour les styles
• TypeScript : pour les scripts depuis la version 2
TypeScript ?
• langage de programmation orienté objet à classes, open source influencé
par C# et JavaScript
• développé et présenté par MicroSoft en 2012
• par rapport au JavaScript, il supporte l'ES6 et il permet de :
• typer les variables
• définir des classes et des interfaces
• utiliser les annotations (les décorateurs)
• exporter et importer des modules
Boudriga Imed 8
INTRODUCTION
Quels langages utilise Angular?
• HTML pour les vues
• CSS pour les styles
• TypeScript : pour les scripts depuis la version 2
TypeScript ?
• langage de programmation orienté objet à classes, open source influencé
par C# et JavaScript
• développé et présenté par Microsoft en 2012
• par rapport au JavaScript, il supporte l'ES6 et il permet de :
• typer les variables
• définir des classes et des interfaces
• utiliser les annotations (les décorateurs)
• exporter et importer des modules
Boudriga Imed 9
INTRODUCTION
Les différentes versions d' Angular
Angular 1 (ou AngularJS) présenté en 2009 : utilisant JavaScript
Angular 2 présenté en octobre 2014 : remplacement du JavaScript par
TypeScript
Angular 4 présenté en décembre 2016
Angular 5 présenté en novembre 2017
Angular 6 présenté en mai 2018
Angular 7 présenté en octobre 2018
Angular 8 présenté en mai 2019
Angular 9 présenté en février 2020
Angular 10 présenté en juin 2020
Angular 11 présenté en Novembre 2020
Etc….
Boudriga Imed 10
Boudriga Imed 11
Installation
• Télécharger et installer NodeJS (Derniere version stable LTS)
• Démarrer une console
• Lancer la commande npm install -g @angular/cli : permet d'installer la
dernière version disponible d' Angular
• Redémarrer la console et vérifier l'installation en exécutant la commande
ng version
• Pour explorer la liste des commandes Angular disponibles, exécuter la
commande ng
• Pour préciser une version d'Angular (par exemple 6.1.0) au moment de
l'installation, on lance la commande npm install -g @angular/
[email protected] Boudriga Imed 12
Installation
Lancer la commande ng new cours-angular
et répondre aux questions suivantes (depuis la version 7) :
Would you like to add Angular routing? (Yes)
Which stylesheet format would you like to use? (CSS)
Se déplacer dans le projet cd cours-angular
Lancer le projet ng serve --open
Boudriga Imed 13
Boudriga Imed 14
Création et structure d'un projet
nous apprenons une abstraction utile selon laquelle les composants sont similaires
aux blocs Lego - nous construisons des composants puis les utilisons pour les coller
ensemble pour créer une application. Nous obtenons également un rappel rapide
sur la famille de langages JavaScript et apprenons où se situe TypeScripts.
Boudriga Imed 15
Création et structure d'un projet
• e2e : contenant les fichiers permettant de tester l'application
• node modules : contenant les fichiers nécessaires de la librairie nodeJS pour
un projet Angular
• src : contenant les fichiers sources de l'application
• [Link] : contenant l'ensemble de dépendance de l'application
• .[Link] (ou [Link] depuis la version 6) : contenant les
données concernant la configuration du projet (l'emplacement des fichiers de
démarrage...)
• [Link] : contenant les données sur les règles à respecter par le
développeur (nombre de caractères max par ligne, l'emplacement des
espaces...)
• [Link] : contenant les données de configuration de TypeScript
Boudriga Imed 16
Création et structure d'un projet
• assets : l'unique dossier accessible aux visiteurs et contenant les images, les
sons...
• [Link] : l'unique fichier HTML d'une application Angular
• [Link] : la feuille de style commune de tous les composants web de
l'application
• [Link] : le logo d'Angular
• app : contient initialement les 5 fichiers du module principal
✓ [Link] : la classe correspondante au module principal
✓ [Link] : la classe associée au composant web
✓ [Link] : le fichier contenant le code HTML
associé au composant web
✓ [Link] : le fichier contenant le code CSS associé
au composant web
✓ [Link] : le fichier de test du composant web
Pour créer un projet sans les fichiers de test (.[Link]), utiliser la commande :
ng new ProjectName --skip-tests=true
H&
Boudriga Imed 17
Components & Modules
le diagramme du code angulaire pourrait ressembler à ceci.
Boudriga Imed 18
Création et structure d'un projet
Les composants sont constitués de code et de modèle HTML et peuvent avoir un
sélecteur, nous pouvons donc l'appeler dans notre HTML.
<appcomponent></appcomponent>
Chaque composant se compose de:
L'un des avantages d'Angular est qu'il est très prévisible. Une fois que vous avez
appris à créer votre premier composant, vous êtes sur la bonne voie pour créer
des composants supplémentaires.
Boudriga Imed 19
Création et structure d'un projet
Contenu d'[Link]
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>ProjectName</title>
<!-- cette balise va permettre d'assurer le routage -->
<base href="/">
<meta name="viewport" content="width=device-width, initialscale=
1">
<link rel="icon" type="image/x-icon" href="[Link]">
</head>
<body>
<!-- le composant web introduit dans le module principal -->
<app-root></app-root>
</body>
</html>
Boudriga Imed 20
Création et structure d'un projet
Contenu de [Link]
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
• @NgModule : pour déclarer cette classe comme module
• declarations : dans cette section, on déclare les composants de ce module
• imports : dans cette section, on déclare les modules nécessaires pour le
module
• providers : dans cette section, on déclare les services qui seront utilisés dans
le module
• bootstrap : dans cette section, on déclare le composant principal de ce
module
Boudriga Imed 21
Création et structure d'un projet
Contenu d'[Link]
Le fichier [Link] and Training Le fichier [Link]
Training
@Component({
selector: 'app-root', <div style="text-align:center">
templateUrl: './[Link]', <h1>
styleUrls: ['./[Link]'] Welcome to {{ title }}!
}) </h1>
export class AppComponent { ...
title = 'cours-angular'; </div>
}
• @Component : pour déclarer cette classe comme composant web
• selector : pour définir le nom de balise correspondant à ce composant web
• templateUrl : pour indiquer le fichier contenant le code HTML
correspondant au composant web
• styleUrls : pour indiquer le(s) fichier(s) contenant le code CSS correspondant
au composant web
Welcome to {{ title }} !: title sera remplacé par sa valeur dans [Link]
H&
Boudriga Imed 22
Boudriga Imed 23
Affichage d'attribut (de type objet ou tableau) ou méthode : Interpolation
• Créer un répertoire classes dans app ou on va placer toutes les classes
• Créer une première classe Personne ayant les attributs num, nom et prenom
• Créer un objet de cette classe dans [Link]
• Afficher les valeurs de cet objet sous forme de liste dans
[Link]
• On peut aussi faire : ng generate class classes/personne
• Pour générer une classe sans le fichier de test (.[Link]) :
ng generate class classes/personne --skipTests=true
• Remarque :
L'option --skipTests=true peut être utilisée avec la commande generate quel
que soit l'élément généré.
Boudriga Imed 24
Affichage d'attribut (de type objet ou tableau) ou méthode : Interpolation
Contenu de [Link]
export class Personne {
constructor(private _num?: number, private _nom?: string, private_prenom?: string) { }
getnum() {
return this._num;
}
setnum(_num: number) {
this._num = _num;
}
getnom() {
return this._nom;
}
setnom(_nom: string) {
this._nom = _nom;
}
getprenom() {
return this._prenom;
}
setprenom(_prenom: string) {
this._prenom = _prenom;
}
}
Boudriga Imed 25
Affichage d'attribut (de type objet ou tableau) ou méthode : Interpolation
Créer un objet de type Personne dans [Link]
import { Component } from '@angular/core';
import { Personne } from './classes/personne';
@Component({
selector: 'app-root',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class AppComponent {
title = 'cours-angular';
personne: Personne = new Personne(100, 'Boudiga', 'Imed');
constructor() {
}
}
Boudriga Imed 26
Affichage d'attribut (de type objet ou tableau) ou méthode : Interpolation
Afficher l'objet personne dans [Link]
<ul>
<li> Nom : {{ [Link]() }} </li>
<li> Prénom : {{ [Link]() }} </li>
</ul>
• L'écriture suivante : {{ personne }} affiche [object Object]
• On peut utiliser un pipe pour affiche un objet au format JSON
{{ personne | json }}
Le résultat est { "_num": 100, "_nom": "Boudriga", "_prenom": "Imed" }
Boudriga Imed 27
Affichage d'attribut (de type objet ou tableau) ou méthode : Interpolation
Cr´eer un tableau d'entiers tab dans [Link]
@Component({
selector: 'app-root',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class AppComponent {
title = 'cours-angular';
personne: Personne = new Personne(100, 'Boudriga', 'Imed');
tab: number[] = [2, 3, 5, 8];
constructor() {
}
}
Boudriga Imed 28
Affichage d'attribut (de type objet ou tableau) ou méthode : Interpolation
Afficher le tableau tab dans [Link]
<ul>
<li> {{ tab[0] }} </li>
<li> {{ tab[1] }} </li>
<li> {{ tab[2] }} </li>
<li> {{ tab[3] }} </li>
</ul>
Remarques
Ce code est trop répétitif
Et si on ne connaissait pas le nombre d'éléments
Ou si on ne voulait pas afficher tous les éléments
Solution
utiliser les directives (section suivante)
Boudriga Imed 29
Affichage d'attribut (de type objet ou tableau) ou méthode : Interpolation
Ajouter une méthode direBonjour() dans [Link]
import { Component } from '@angular/core';
import { Personne } from './classes/personne';
@Component({
selector: 'app-root',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class AppComponent {
title = 'cours-angular';
personne: Personne = new Personne('Boudriga', 'Imed');
tab: number[] = [2, 3, 5, 8];
constructor() {
}
direBonjour(): string {
return 'bonjour Angular';
}
}
Boudriga Imed 30
Affichage d'attribut (de type objet ou tableau) ou méthode : One way binding
• Appeler la méthode direBonjour() dans [Link]
<p>{{ direBonjour() }}</p>
• Pour afficher le contenu de l'attribut title dans le template, on peut utiliser
l'interpolation
<p> {{ title }} </p>
• On peut aussi faire le one way binding en utilisant la propriété JavaScript
textContent
<p [textContent]=title></p>
• [ one way binding ] ou [ property binding ]
[property] = 'value' : permet de remplacer value par sa valeur dans la
classe [Link]
Boudriga Imed 31
Boudriga Imed 32
Directives Angular
Plusieurs directives possibles
• *ngFor
• *ngIf
• *ngSwitch
• ngStyle
• ngClass
Ces directives s'utilisent conjointement avec les composants web suivant
• ng-container
• ng-template
Boudriga Imed 33
Directives Angular
*ngFor : permet de répéter un traitement (affichage d'un élément HTML)
s'utilise comme un attribut de balise et sa valeur est une instruction itérative
Afficher le tableau tab en utilisant *ngFor
<ul>
<li *ngFor="let elt of tab">
{{ elt }}
</li>
</ul>
Et pour avoir l'indice de l'itération courante
<ul>
<li *ngFor="let elt of tab; let i = index">
{{ i }} : {{ elt }}
</li>
</ul>
Boudriga Imed 34
Directives Angular
aussi pour avoir l'indice de l'itération courante
<ul>
<li *ngFor="let elt of tab; index as i">
{{ i }} : {{ elt }}
</li>
</ul>
On peut aussi utiliser first pour savoir si l'élément courant est le premier de la liste
<ul>
<li *ngFor="let elt of tab; let i = index; let isFirst =first">
{{ i }} : {{ elt }} : {{ isFirst }}
</li>
</ul>
• last : retourne true si l'élément courant est le dernier de la liste, false sinon.
• even : retourne true si l'indice de l'élément courant est pair, false sinon.
• odd : retourne true si l'indice de l'élément courant est impair, false sinon.
H
Boudriga Imed 35
Directives Angular
Considérons la liste suivante ( à déclarer dans [Link]) :
personnes: Array<Personne> = [
new Personne(100, 'Boudriga', 'Imed'),
new Personne(101, 'Boudriga', 'Ali'),
new Personne(102, 'Saleh', 'Ben Saleh'),
new Personne(103, 'Ben Ammar', 'Ammar')
];
Exercice
écrire un script Angular qui permet d'afficher dans une liste HTML les nom
et prénom de chaque personne de la liste personnes .
Boudriga Imed 36
Directives Angular
• Pour tester puis afficher si le premier élément est impair
<ul>
<li *ngIf="tab[0] % 2 != 0">
{{ tab[0] }} est impair
</li>
</ul>
Exercice
écrire un code HTML, en utilisant les directives Angular, qui permet d'afficher le
premier élément du tableau (tab) ainsi que sa parité (pair ou impair).
Boudriga Imed 37
Directives Angular
• Une première solution avec *ngIf et else
<ul>
<li *ngIf="tab[0] % 2 != 0; else sinon">
{{ tab[0] }} est impair
</li>
<ng-template #sinon>
<li>
{{ tab[0] }} est pair
</li>
</ng-template>
</ul>
ng-template n'apparaîtra pas dans le DOM de la page.
Boudriga Imed 38
Directives Angular
Une deuxième solution avec *ngIf, then et else
<ul>
<li *ngIf="tab[0] % 2 != 0; then si else sinon">
Ce code ne sera jamais pris en compte
</li>
<ng-template #si>
<li>
{{ tab[0] }} est impair
</li>
</ng-template>
<ng-template #sinon>
<li>
{{ tab[0] }} est pair
</li>
</ng-template>
</ul>
Exercice
Exercice
écrire un code HTML, en utilisant les directives Angular, qui permet d'afficher sous
forme d'une liste chaque élément du tableau précédent (tab) ainsi que sa parité.
Boudriga Imed 39
Directives Angular
Solution
<ul>
<ng-container *ngFor="let elt of tab">
<li *ngIf="elt % 2 != 0; else sinon">
{{ elt }} est impair
</li>
<ng-template #sinon>
<li>
{{ elt }} est pair
</li>
</ng-template>
</ng-container>
</ul>
ng-container n'apparaîtra pas dans le DOM de la page.
Boudriga Imed 40
Directives Angular
Exercice
écrire un code HTML, en utilisant les directives Angular, qui
permet d'afficher sous forme d'une liste chaque élément du
tableau précédent avec un message défini ainsi :
• Si la valeur est comprise entre 0 et 9 : échec
• Si elle est entre 10 et 13 : moyen
• Si elle est entre 14 et 16 : bien
• Sinon : très bien
Boudriga Imed 41
Directives Angular
Exemple avec ngSwitch
<ul>
<ng-container *ngFor="let elt of tab">
<ng-container [ngSwitch]="elt">
<li *ngSwitchCase="1">
{{ elt }} = un
</li>
<li *ngSwitchCase="2"> Pour comparer avec une chaîne de caractère, il faut
{{ elt }} = deux ajouter les simples quotes (par exemple :
</li> *ngSwitchCase="'bonjour'".
<li *ngSwitchCase="3">
{{ elt }} = trois
</li>
<li *ngSwitchDefault>
{{ elt }} : autre
</li>
</ng-container>
</ng-container>
</ul>
Boudriga Imed 42
Directives Angular
Refaire cet exercice avec ngSwitch
écrire un code HTML, en utilisant les directives Angular, qui
permet d'afficher sous forme d'une liste chaque élément du
tableau précédent avec un message défini ainsi :
Si la valeur est comprise entre 0 et 9 : échec
Si elle est entre 10 et 13 : moyen
Si elle est entre 14 et 16 : bien
Sinon : trés bien
Boudriga Imed 43
Directives Angular
Solution
<ul>
<ng-container *ngFor="let elt of tab">
<ng-container [ngSwitch]="true">
<li *ngSwitchCase="elt >= 0 && elt < 10">
{{ elt }} : échec
</li> *ngFor nous permet d'itérer sur un tableau,
<li *ngSwitchCase="elt >= 10 && elt < 13"> mais non pas sur un objet,
{{ elt }} : moyen Il est possible d'itérer sur un objet après avoir
</li>
défini un pipe qui convertit virtuellement un
<li *ngSwitchCase="elt >= 13 && elt < 15">
objet en tableau
{{ elt }} : bien
</li>
<li *ngSwitchCase="elt >= 15">
{{ elt }} : très bien
</li>
<li *ngSwitchDefault>
autre
</li>
</ng-container>
</ng-container>
</ul>
Boudriga Imed 44
Directives Angular
ngStyle
• permet de modifier le style d'un élément HTML
• s'utilise conjointement avec le property binding pour récupérer des valeurs
définies dans la classe
Considérons le contenu suivant de [Link]
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class AppComponent {
nom = 'Boudriga';
couleur = 'blue';
// le contenu précédent
}
Pour afficher le contenu de l'attribut nom dans le template avec une couleur de
fond rouge : <p [textContent]=nom [ngStyle]="{backgroundColor: 'red'}"></p>
Boudriga Imed 45
Directives Angular
Pour utiliser une couleur définie dans un attribut de la classe associée
<p [textContent]=nom [ngStyle]="{backgroundColor:couleur}"></p>
Il est possible de définir des méthodes dans [Link] qui gèrent le style d'un
élément HTML
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class AppComponent {
// le contenu précédent
getColor(): string {
return 'white';
}
getBackgroundColor(): string {
return 'red';
}
}
Boudriga Imed 46
Directives Angular
Pour afficher le contenu de l'attribut nom dans le template avec une couleur de fond rouge
<p [ngStyle]="{color: getColor(), backgroundColor: getBackgroundColor()}">
{{ nom }}
</p>
Boudriga Imed 47
Directives Angular
ngClass
• permet d'attribuer de nouvelles classes d'un élément HTML
• s'utilise conjointement avec le property binding pour récupérer des valeurs définies
dans la classe ou dans la feuille de style
Considérons le contenu suivant de [Link]
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class AppComponent {
// le contenu précédent
}
Boudriga Imed 48
Directives Angular
On définit deux classes rouge et bleu dans [Link]
.rouge {
color: red;
}
.bleu {
color: blue;
}
Pour associer la classe rouge à la balise <p>
<p [ngClass]="{'rouge': true}">{{ nom }} </p>
On peut aussi appeler une méthode dans la directive ngClass
<p [ngClass]="{'rouge': afficherEnRouge()}"> {{ nom }} </p>
Définissons la méthode dans [Link]
afficherEnRouge(): boolean {
return [Link] === 'blue' ? true : false;
}
Boudriga Imed 49
Directives Angular
Ainsi, on peut faire aussi un affichage conditionnel
<p [ngClass]="{'rouge': nom == 'Boudriga'}">{{ nom }} </p>
Ou encore (le paragraphe sera affiché en rouge si nom contient
Boudriga, en bleu sinon)
<p [ngClass]="{'rouge': nom == 'Boudriga', 'bleu': nom!= 'Boudriga'}">
{{ nom }}
</p>
On peut aussi utiliser les expressions ternaires
<p [ngClass]="nom == 'Boudriga' ? 'rouge' : 'bleu'">
{{ nom }}
</p>
Exercice
Utiliser ngClass dans un code HTML permettant d'afficher en bleu
les éléments pairs du tableau précédent (tab) et en rouge les éléments impairs.
Boudriga Imed 50
Directives Angular
Première solution
<ul>
<ng-container *ngFor="let elt of tab">
<li [ngClass]="{'rouge': elt % 2 != 0, 'bleu': elt % 2 ==
0}">
{{ elt }}
</li>
</ng-container>
</ul>
Deuxième solution
<ul>
<ng-container *ngFor="let elt of tab">
<li [ngClass]="elt % 2 == 0 ? 'bleu' : 'rouge'">
{{ elt }}
</li>
</ng-container>
</ul>
Boudriga Imed 51
Directives Angular
Troisième solution
<ul>
<ng-container *ngFor="let elt of tab">
<li *ngIf="elt%2==0" [ngClass]="{'rouge': !estPair(
elt), 'bleu': estPair(elt)}">
{{ elt }}
</li>
</ng-container>
</ul>
Dans [Link], on définit la méthode estPair
estPair(elt: number): boolean {
return elt % 2 === 0;
}
: Res
Boudriga Imed 52
Directives Angular
Première solution
<ul>
<ng-container *ngFor="let elt of personnes; let isEven = even; let isOdd = odd">
<li [ngClass]="{'rouge': isEven,'bleu': isOdd}">
{{ [Link] }} {{ [Link] }}
</li>
</ng-container>
</ul>
Deuxième solution
<ul>
<ng-container *ngFor="let elt of personnes; let i = index;">
<li [ngClass]="i % 2 != 0 ? 'rouge' : 'bleu'">
{{ [Link] + " " + [Link] }}
</li>
</ng-container>
</ul>
Boudriga Imed 53
Directives Angular
Troisième solution
<ul>
<ng-container *ngFor="let personne of personnes">
<li [ngStyle]="{color: getNextColor()}">
{{ [Link] }} {{ personne. nom }}
</li>
</ng-container>
</ul>
Dans [Link], on définit la méthode getNextColor
couleur = 'blue';
getNextColor(): string {
[Link] = [Link] === 'red' ? 'blue' : 'red';
return [Link];
}
Boudriga Imed 54
Boudriga Imed 55
Commandes utiles
Pour désinstaller Angular
npm uninstall -g @angular/cli
Pour vider le cache
# à partir de la version 5 de NPM
npm cache verify
# Avant la version 5
npm cache clean
Pour mettre à jour la version d'Angular
ng update @angular/cli @angular/core
Pour mettre à jour tous les paquets définis dans [Link]
ng update @angular/cli @angular/core --all=true
Boudriga Imed 56
Commandes utiles
Angular propose de nombreuses commandes pour générer du code,
ou comme on le nomme outre-Atlantique, le scaffolding. Il est ainsi
possible de créer de nouveaux Components, Directives…
SCAFFOLD USAGE
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
Enum ng g enum my-new-enum
Module ng g module my-module
Boudriga Imed 57
Boudriga Imed 58
Les PIPES Angular
Définition : Un pipe
• a le rôle de formater et modifier l'affichage d'une donnée dans le
.[Link]
• est une classe décorée par le décorateur @pipe
• doit implémenter la méthode transform(value: any, args?: any) de l'interface
PipeTransform
• peut prendre un ou plusieurs paramètres
Exemple
{{ "bonjour" | uppercase }}
<!-- BONJOUR -->
Certains pipes peuvent prendre des paramètres
{{ maDate | date:'d MMM y' }}
<!-- affiche 05 dec 2018 -->
Il est possible d'enchaîner les pipes
{{ maDate | date:'d MMM y' | uppercase }}
<!-- affiche 05 DEC 2020 -->
Boudriga Imed 59
Les PIPES Angular
Les pipes
• pipes prédéfinis comme uppercase, lowercase...
• pipes personnalisés
Pour créer un pipe ng generate pipe nom-pipe
Ou le raccourci ng g p nom-pipe
Exemple : définissons un pipe (get-char) qui retourne la première lettre d'une
chaîne de caractères
ng g p pipes/get-char
Constat
• : deux fichiers générés
[Link]
[Link]
• déclaration du pipe dans [Link]
Boudriga Imed 60
Les PIPES Angular
Contenu du [Link]
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'getChar'
})
export class GetCharPipe implements PipeTransform {
transform(value: any, ...args: any[]): any {
return null;
}
}
value correspond à la valeur de la chaîne qu'on va modifier.
Boudriga Imed 61
Les PIPES Angular
Modifions [Link] pour retourner la première lettre
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'getChar'
})
export class GetCharPipe implements PipeTransform {
transform(value: any, ...args: any[]): any {
return value[0];
}
}
Pour tester, écrire dans un .[Link]
{{ "bonjour" | getChar }}
<!-- affiche b -->
{{ « Imed" | getChar }}
<!-- affiche I -->
{{ « Boudriga" | getChar }}
<!-- affiche B -->
Boudriga Imed 62
Les PIPES Angular
Modifions [Link] pour retourner un caractère à une position donnée (pos)
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'getChar'
})
export class getCharPipe implements PipeTransform {
transform(value: string, ...args: number[]):unknown {
if (args[0] && args[0] < [Link]) {
return value[args[0]];
}
return value[0];
}}
Pour tester, écrire dans un .[Link]
{{ "bonjour" | getChar:2 }}
<!-- affiche n -->
{{ « Imed" | getChar:3 }}
<!-- affiche d -->
{{ « Boudriga" | getChar }}
<!-- affiche B --> Boudriga Imed 63
Les PIPES Angular
Exercice 1
Ecrire un pipe qui permet de transformer un objet en tableau pour qu'on puisse itérer
avec un *ngFor
L'objectif est de pouvoir parcourir un objet comme s'il s'agit d'un tableau (obj est un
objet défini dans .[Link])
<ul>
<li *ngFor="let elt of personne | objToArray">
{{ elt }} : {{ personne[elt] }}
</li>
</ul>
Boudriga Imed 64
Les PIPES Angular
Exercice 2
Créer un pipe sub-array qui permet de retourner un sous tableau à partir d'un tableau
selon les deux indices passés en paramètres
• si aucun indice n'est présent, il retourne le tableau complet
• si un seul indice est présent, on retourne le sous-tableau qui commence de cet indice
jusqu' à la fin
Boudriga Imed 65
Boudriga Imed 66
Les Services Angular
Service
• classe TypeScript décorée par @Injectable
• singleton
• pouvant avoir le rôle de :
- l'intermédiaire avec la partie back-end
-un moyen de communication entre composants
• injectable dans les classes où on a besoin de l'utiliser
• pouvant utiliser un ou plusieurs autres services
Quelques services Angular utilisés
• ActivatedRoute
• Router
• FormBuilder
• HttpClient
• ………
Il est aussi possible de créer nos services personnalisés.
Boudriga Imed 67
Les Services Angular
Pour créer un service : ng generate service nom-service
Ou aussi : ng g s nom-service
Créons un service personne dans un répertoire services
ng g s services/personne
Constat : deux fichiers créés
-[Link]
-[Link]
Boudriga Imed 68
Les Services Angular
Le contenu de [Link] jusqu'à la version 5 d'Angular
import { Injectable } from '@angular/core';
@Injectable()
export class PersonneService {
constructor() { }
}
Le contenu de [Link] à partir de la version 6 d'Angular
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class PersonneService {
constructor() { }
}
:R
Boudriga Imed 69
Les Services Angular
Pour les versions d'Angular inférieures à la version 6, il faut déclarer le service dans la
section providers dans [Link]
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
// + les imports précédents
import { PersonneService } from './services/[Link]';
@NgModule({
declarations: [
AppComponent,
AdresseComponent, Remarque
PersonneComponent, • Depuis la version 6 d'Angular, on n'a plus
FormulaireComponent besoin de déclarer les services dans la section
], providers dans [Link]
imports: [ • L'expression providedIn: 'root' vaut déclaration
BrowserModule,
FormsModule,
],
providers: [PersonneService],
bootstrap: [AppComponent]
})
export class AppModule { }
Boudriga Imed 70
Les Services Angular
Commençons par créer l'interface personne :
ng g i interfaces/personne
Mettons à jour le contenu de [Link]
export interface Personne {
id?: number;
nom?: string;
prenom?: string;
}
Boudriga Imed 71
Les Services Angular
Mettons à jour le contenu de [Link]
import { Injectable } from '@angular/core';
import { Personne } from '../interfaces/personne';
@Injectable({
providedIn: 'root'
})
export class PersonneService {
personnes: Array<Personne> = new Array<Personne>();
constructor() { }
getAll(): Array<Personne> {
return [Link];
}
addPerson(p: Personne): void {
[Link](p);
}
}
Il faut aussi créer les méthodes qui permettent de modifier et supprimer de personnes.
Boudriga Imed 72
Les Services Angular
Pour la suite, créons un composant personne dans composants
ng g c composants/personne
Résultat
CREATE src/app/composants/personne/[Link] (23 bytes)
CREATE src/app/composants/personne/[Link] (642 bytes)
CREATE src/app/composants/personne/[Link] (277 bytes)
CREATE src/app/composants/personne/[Link] (0 bytes)
UPDATE src/app/[Link] (1226 bytes)
Boudriga Imed 73
Les Services Angular
Contenu de [Link]
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-personne',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class PersonneComponent implements OnInit {
constructor() { }
ngOnInit() { }
}
Boudriga Imed 74
Les Services Angular
Injectons PersonneService dans le constructeur de PersonneComponent pour pouvoir
l'utiliser
import { Component, OnInit } from '@angular/core';
import { PersonneService } from '../../services/personne.
service';
@Component({
selector: 'app-personne',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class PersonneComponent implements OnInit {
constructor(private personneService: PersonneService) { }
ngOnInit() { }
}
Remarque
Maintenant on peut utiliser les méthodes définies dans le service Il est à rappeler que
le service et l'élément de notre application qui va communiquer avec un serveur JSON
ou un Web-Service (pour persister ou récupérer des données)
Boudriga Imed 75
Les Services Angular
Pour tester, on prépare la liste des personne dans [Link]
import { Injectable } from '@angular/core';
import { Personne } from '../interfaces/personne';
@Injectable({
providedIn: 'root'
})
export class PersonneService {
personnes: Array <Personne> = new Array <Personne>();
constructor() {
[Link]({ nom: 'Boudriga', prenom: 'imed' });
[Link]({ nom: 'Saleh', prenom: 'ben saleh' });
}
getAll(): Array<Personne> {
return [Link];
}
addPerson(p: Personne): void {
[Link](p);
}
}
Boudriga Imed 76
Les Services Angular
Dans [Link], on appelle la méthode getAll() du service
import { Component, OnInit } from '@angular/core';
import { PersonneService } from '../../services/personne.
service';
import { Personne } from '../../interfaces/personne';
@Component({
selector: 'app-personne',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class PersonneComponent implements OnInit {
personnes: Array <Personne> = new Array <Personne>();
constructor(private personneService: PersonneService) { }
ngOnInit() {
[Link] = [Link]();
}
Boudriga Imed 77
Les Services Angular
Enfin, on affiche le résultat dans [Link]
<ul>
<li *ngFor="let elt of personnes">
{{ [Link] }} {{ [Link] }}
</li>
</ul>
Boudriga Imed 78
Boudriga Imed 79
Les formulaires
Définition : Un formulaire
• un outil graphique que nous créons avec le langage de description HTML
• il permet à l'utilisateur de saisir des données
• et de les envoyer vers une autre page, vers une base de données
Alors pourquoi Angular?
Angular nous facilite
• la récupération des données saisies
• la validation et le contrôle des valeurs saisies
• la gestion d'erreurs
...
Que propose Angular ?
• Template-driven forms : utilisant FormsModule, facile et conseillé pour les formulaires
simples
• Reactive forms basé sur ReactiveFormsModule : robuste, évolutif et conçu pour les
applications nécessitant des contrôles particuliers
Form Group
Form Builder
Boudriga Imed 80
Les formulaires
Un événement
• action appliquée par l'utilisateur ou simulée par le développeur sur un
.[Link]
• un événement déclenché => une méthode, dans le .[Link]
associé, exécutée
Pour commencer
• créer un composant formulaire
• créer un chemin /formulaire permettant d'afficher ce composant
Boudriga Imed 81
Les formulaires
Le fichier [Link]
import { Component, OnInit } from '@angular/
core';
@Component({
selector: 'app-formulaire',
templateUrl: './[Link]'
,
styleUrls: ['./[Link]']
})
export class FormulaireComponent implements
OnInit {
constructor() { }
ngOnInit() {
}
}
Le fichier [Link]
<p>
formulaire works!
</p>
Boudriga Imed 82
Les formulaires
Le fichier [Link]
import { Component, OnInit } from '@angular/
core';
@Component({
selector: 'app-formulaire',
templateUrl: './[Link]'
,
styleUrls: ['./[Link]']
})
export class FormulaireComponent implements
OnInit {
constructor() { }
ngOnInit() { }
direBonjour(){
[Link]("Bonjour");
}
}
Le fichier [Link]
<div> <button (click)=« direBonjour()">
Cliquer </button> </div>
Boudriga Imed 83
Les formulaires
(eventBinding)
La valeur de l'attribut événement situé entre () sera le nom d'une méthode de
FormulaireComponent
Explication
En cliquant sur le bouton cliquer, la méthode direBonjour() est exécutée.
Bonjour est affiché dans la console.
Et si on veut récupérer la valeur saisie dans une zone texte et l'afficher dans une
autre partie du composant.
Boudriga Imed 84
Les formulaires
Le fichier [Link]
import { Component, OnInit } from '@angular/
core';
@Component({
selector: 'app-formulaire',
templateUrl: './[Link]'
,
styleUrls: ['./[Link]']
})
export class FormulaireComponent implements
OnInit {
result = "";
constructor() { }
ngOnInit() { }
direBonjour(nom:string){
[Link] = nom;
}
}
Boudriga Imed 85
Les formulaires
Le fichier [Link]
<div>
<input type=text #nom>
</div>
<div>
<button (click)="
direBonjour(nom.
value)">
cliquer
</button>
</div>
<div>
Bonjour {{ result }}
</div>
#nom est une variable locale contenant les attributs de cet élément HTML. Les variables
locales peuvent être utilisées avec tout élément HTML, <p>, <h>...
Boudriga Imed 86
Les formulaires
Remarque
Il est possible de simplifier le code précédant en utilisant le two way binding
Plusieurs formes de binding
• {{ interpolation }} : permet de récupérer la valeur d'un attribut déclarée dans le
.[Link]
• [ one way binding ] : permet aussi de récupérer la valeur d'un attribut déclarée dans
le .[Link]
• ( event binding ) : permet au .[Link] de récupérer des valeurs passées par le
.[Link]
{{ interpolation }} est un raccourci de [ one way binding ]
<p [textContent]= "result"></p>
<p> {{ result }} </p>
<!-- Les deux écritures sont équivalentes -->
Boudriga Imed 87
Les formulaires
Il est possible de combiner one way binding et event binding
• Résultat : two way binding
• Un changement de valeur dans .[Link] sera aperçu dans .[Link] et un
changement dans .[Link] sera reçu dans .[Link]
two way binding
• Pour la liaison bidirectionnelle, il nous faut la propriété ngModel
• Pour pouvoir utiliser la propriété ngModel, il faut ajouter le module FormsModule
dans [Link]
Boudriga Imed 88
Les formulaires
Nouveau contenu d'[Link]
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
//+ les autres imports
@NgModule({
declarations: [
AppComponent,
AdresseComponent,
PersonneComponent,
FormulaireComponent
],
imports: [
BrowserModule,
FormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Boudriga Imed 89
Les formulaires
Le fichier [Link]
import { Component, OnInit } from '@angular/
core';
@Component({
selector: 'app-formulaire',
templateUrl: './[Link]'
,
styleUrls: ['./[Link]']
})
export class FormulaireComponent implements
OnInit {
nom = "";
result = "";
constructor() { }
ngOnInit() { }
direBonjour(){
[Link] = [Link];
}
}
Boudriga Imed 90
Les formulaires
Le fichier [Link]
<div>
<input type=text [(
ngModel)]=nom>
</div>
<div>
<button (click)="
direBonjour()">
cliquer
</button>
</div>
<div>
Bonjour {{ result }}
</div>
Nous n'avons pas besoin de cliquer pour envoyer la valeur saisie dans le champ texte,
elle est mise à jour simultanément dans la classe quand elle est modifiée dans la vue.
Boudriga Imed 91
Les formulaires
Le fichier [Link]
import { Component, OnInit } from '@angular/
core';
@Component({
selector: 'app-formulaire',
templateUrl: './[Link]'
,
styleUrls: ['./[Link]']
})
export class FormulaireComponent implements
OnInit {
nom = "";
constructor() { }
ngOnInit() { }
}
Le fichier [Link]
<div><input type=text [(ngModel)]=nom></div>
<div>Bonjour {{ nom }}</div>
Boudriga Imed 92
Les formulaires
Commenc¸ons par créer une interface personne qui va nous servir de modèle
ng generate interface interfaces/personne
On peut aussi utiliser le raccourci
ng g i interfaces/personne
Mettons à jour le contenu de [Link]
export interface Personne {
id?: number;
nom?: string;
prenom?: string;
}
Boudriga Imed 93
Les formulaires
Considérant le formulaire [Link]
<form>
<div>
Nom : <input type=text name=nom [(ngModel)]=[Link]>
</div>
<div>
Prénom : <input type=text name=prenom [(ngModel)]=[Link]>
</div>
<div>
<button>
Ajouter
</button>
</div>
</form>
Pour utiliser ngModel dans un formulaire, il faut définir une valeur pour l'attribut
name de chaque élément du formulaire.
Boudriga Imed 94
Les formulaires
Le fichier [Link]
import { Component, OnInit } from '@angular/core';
import { Personne } from '../../interfaces/personne';
@Component({
selector: 'app-formulaire',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class FormulaireComponent implements OnInit {
personne: Personne = { };
constructor() { }
ngOnInit() { }
}
Pour soumettre le formulaire, il faut qu'il soit valide. Supposant que
• les deux zones textes sont obligatoires
• le bouton doit être initialement désactivé, on l'active que lorsque le formulaire est
valide
Boudriga Imed 95
Les formulaires
Commençons par rendre les deux zones textes obligatoires et désactivons le bouton
<form>
<div>
Nom : <input type=text name=nom [(ngModel)]=[Link] required>
</div>
<div>
Prénom : <input type=text name=prenom [(ngModel)]=[Link] required>
</div>
<div>
<button disabled>
Ajouter
</button>
</div>
</form>
Question : comment réactiver le bouton?
• Ecrire une fonction JavaScript pour tester si les deux champs ne sont pas vides
• Ecrire une méthode dans la classe qui vérifiera à chaque saisie si les deux champs ne
sont pas vides pour réactiver le bouton
Boudriga Imed 96
Les formulaires
Avec les directives Angular, il y a plus simple
• Utiliser la directive ngForm pour créer une variable locale associée au formulaire
• Exploiter la propriété valid de ngForm pour valider le formulaire
Pour le réactiver
<form #monForm=ngForm>
<div>
Nom : <input type=text name=nom [(ngModel)]=[Link] required>
</div>
<div>
Prénom : <input type=text name=prenom [(ngModel)]=[Link] required>
</div>
<div>
<button [disabled]=![Link]>
Ajouter
</button>
</div>
</form>
Boudriga Imed 97
Les formulaires
Question : et si on veut afficher en rouge les champs obligatoires non-renseignés?
• Utiliser les variables locales
• Exploiter les propriétés CSS fournies par Angular
Utilisons les variables locales et affichons les classes CSS associées attribuées par Angular
<form #monForm=ngForm>
<div>
Nom : <input type=text name=nom [(ngModel)]=[Link] required #nom>
</div>
{{ [Link]}}
<div>
Prénom : <input type=text name=prenom [(ngModel)]=[Link] required
#prenom>
</div>
{{ [Link]}}
<div>
<button [disabled]=![Link]>
ajouter
</button>
</div>
</form> Boudriga Imed 98
Les formulaires
Les classes CSS affichées pour les deux champs
• ng-untouched : classe Angular appliquée quand le champ n'est pas touché (son inverse
est ng-touched)
• ng-pristine : classe Angular appliquée quand le champ est vide (son inverse est ng-dirty)
• ng-invalid : classe Angular appliquée quand le champ n'est pas valid (son inverse est ng-
valid)
Nettoyons le code précèdent
<form #monForm=ngForm>
<div>
Nom : <input type=text name=nom [(ngModel)]=[Link] required #nom>
</div>
<div>
Prénom : <input type=text name=prenom [(ngModel)]=[Link] required
#prenom>
</div>
<div>
<button [disabled]=![Link]>
ajouter
</button>
</div>
</form> Boudriga Imed 99
Les formulaires
Définissons des propriétés pour quelques classes CSS fournies par Angular
.ng-invalid:not(form){
border-left: 5px solid red;
}
.ng-valid:not(form){
border-left: 5px solid green;
}
Boudriga Imed 100
Les formulaires
On peut aussi afficher un message en cas de violation de contrainte
<form #monForm=ngForm>
<div>
Nom : <input type=text name=nom [(ngModel)]=[Link] required
#nom="ngModel">
</div>
<div [hidden]="[Link]"> Le nom est obligatoire</div>
<div>
Prénom : <input type=text name=prenom [(ngModel)]=[Link]
required #prenom="ngModel"> </div>
<div [hidden]="[Link]">Le prénom est obligatoire</div>
<div>
<button [disabled]=![Link]>
ajouter
</button>
</div>
</form>
Boudriga Imed 101
Les formulaires
Pour ne pas afficher les messages d'erreur au chargement de la page
<form #monForm=ngForm>
<div>
Nom : <input type=text name=nom [(ngModel)]=[Link] required #
nom="ngModel">
</div>
<div [hidden]="[Link] || [Link]">Le nom est obligatoire</div>
<div>
Prénom : <input type=text name=prenom [(ngModel)]=[Link]
required #prenom="ngModel">
</div>
<div [hidden]="[Link] || [Link]">Le prénom est obligatoire</div>
<div>
<button [disabled]=![Link]>
ajouter
</button>
</div>
</form>
Boudriga Imed 102
Les formulaires
Pour soumettre un formulaire, on utilise la directive ngSubmit
<form #monForm=ngForm (ngSubmit)=ajouterPersonne()>
<div>
Nom : <input type=text name=nom [(ngModel)]=[Link] required #
nom="ngModel">
</div>
<div [hidden]="[Link] || [Link]">Le nom est obligatoire</div>
<div>
Prénom : <input type=text name=prenom [(ngModel)]=[Link]
required #prenom="ngModel">
</div>
<div [hidden]="[Link] || [Link]">Le prénom est obligatoire</div>
<div>
<button [disabled]=![Link]>
ajouter
</button>
</div>
</form>
Boudriga Imed 103
Les formulaires
Le fichier [Link]
import { Component, OnInit } from '@angular/core';
import { Personne } from '../../interfaces/personne';
@Component({
selector: 'app-formulaire',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class FormulaireComponent implements OnInit {
personnes: Array<Personne> = [];
personne: Personne = { };
constructor() { }
ngOnInit(): void { }
ajouterPersonne(): void {
[Link]({ ...[Link] });
[Link] = '';
[Link] = '';
[Link]([Link]);
}
}
Boudriga Imed 104
Les formulaires
• créer un composant form
• Créer un chemin /form permettant d'afficher ce composant
• ajouter ReactiveFormsModule dans la section imports de [Link]
FormControl
• Une classe Angular
• Permettant d'associer un attribut de composant à un champ de formulaire défini
dans le template associé afin de faciliter
✓ le binding
✓ le contrôle et la validation
Boudriga Imed 105
Les formulaires
Dans [Link], déclarons un attribut nom de type FormControl
import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
@Component({
selector: 'app-form',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class FormComponent implements OnInit {
nom = new FormControl('');
constructor() { }
ngOnInit() { }
}
Dans [Link], on aura un champ de formulaire associé à l'objet nom.
Boudriga Imed 106
Les formulaires
Dans [Link], on ajoute un champ associé à l'attribut nom
<div>
Nom :
<input type="text" [formControl]="nom">
</div>
On peut ajouter un bouton avec un event binding
<label>
Nom :
<input type="text" [formControl]="nom">
</label>
<button (click)='afficherNom()'>cliquer</button>
N'oublions pas de définir la méthode afficherNom() dans [Link].
Boudriga Imed 107
Les formulaires
Dans [Link], on ajoute la méthode afficherNom()
import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
@Component({
selector: 'app-form',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class FormComponent implements OnInit {
nom = new FormControl('');
constructor() { }
ngOnInit(): void { }
afficherNom(): void {
[Link]([Link]);
}
}
Boudriga Imed 108
Les formulaires
Dans [Link], on ajoute la méthode afficherNom()
import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
@Component({
selector: 'app-form',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class FormComponent implements OnInit {
nom = new FormControl('');
constructor() { }
ngOnInit(): void { }
afficherNom(): void {
[Link]([Link]);
}
}
En cliquant sur le bouton, la valeur saisie dans le champ texte sera affichée dans la
console.
Boudriga Imed 109
Les formulaires
Dans [Link], on peut utiliser le constructeur de FormControl pour définir une
valeur initiale à afficher au chargement du composant
import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
@Component({
selector: 'app-form',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class FormComponent implements OnInit {
nom = new FormControl('boudriga');
constructor() { }
ngOnInit() { }
afficherNom() {
[Link]([Link]);
}
}
Boudriga Imed 110
Les formulaires
FormGroup
Une classe Angular Composée de plusieurs objets de type FormControl
Dans [Link], commençons par définir un objet de type FormGroup
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
@Component({
selector: 'app-form',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class FormComponent implements OnInit {
personneForm = new FormGroup({
id: new FormControl(''),
nom: new FormControl(''),
prenom: new FormControl('')
});
constructor() { }
ngOnInit(): void { }
}
Boudriga Imed 111
Les formulaires
Dans [Link], créons maintenant le formulaire prenant les trois éléments
déclarés dans le FormGroup
<form [formGroup]="personneForm">
<div>
Identifiant :
<input type="number" formControlName="id">
</div>
<div>
Nom :
<input type="text" formControlName="nom">
</div>
<div>
Prénom :
<input type="text" formControlName="prenom">
</div>
</form>
Boudriga Imed 112
Les formulaires
On peut ajouter un bouton avec un event binding
<form [formGroup]="personneForm">
<div>
Identifiant :
<input type="number" formControlName="id">
</div>
<div>
Nom :
<input type="text" formControlName="nom">
</div>
<div>
Prénom :
<input type="text" formControlName="prenom">
</div>
<button (click)='afficherTout()'>cliquer</button>
</form>
Boudriga Imed 113
Les formulaires
On peut ajouter un bouton avec un event binding
<form [formGroup]="personneForm">
<div>
Identifiant :
<input type="number" formControlName="id">
</div>
<div>
Nom :
<input type="text" formControlName="nom">
</div>
<div>
Pr´enom :
<input type="text" formControlName="prenom">
</div>
<button (click)='afficherTout()'>cliquer</button>
</form>
N'oublions pas de définir la méthode afficherTout() dans [Link].
Boudriga Imed 114
Les formulaires
Dans [Link], ajoutons la méthode afficherTout()
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
@Component({
selector: 'app-form',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class FormComponent implements OnInit {
personneForm = new FormGroup({
id: new FormControl(''),
nom: new FormControl(''),
prenom: new FormControl('')
});
constructor() { }
ngOnInit(): void { }
afficherTout(): void {
[Link]([Link]);
}
}
Boudriga Imed 115
Les formulaires
Pour récupérer le FormControl associé à nom
[Link]([Link]);
Ou aussi
[Link]([Link]('nom'));
Pour vider les champs d'un formulaire
[Link]();
Boudriga Imed 116
Les formulaires
On peut initialiser les champs du formulaire avec la méthode setValue()
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
@Component({
selector: 'app-form',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class FormComponent implements OnInit {
personneForm = new FormGroup({
id: new FormControl(''),
nom: new FormControl(''),
prenom: new FormControl('')
});
constructor() { }
ngOnInit() {
[Link]({nom: 'boudriga', prenom: 'imed', id: 1});
}
afficherTout() {
[Link]([Link]);
}
} Boudriga Imed 117
Les formulaires
valueChanges permet de surveiller le changement de valeur d'un champ du formulaire
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
@Component({
selector: 'app-form',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class FormComponent implements OnInit {
personneForm = new FormGroup({
id: new FormControl(''),
nom: new FormControl(''),
prenom: new FormControl('')
});
constructor() { }
ngOnInit() {
[Link](change => {
[Link](change);
});
}
afficherTout() { [Link]([Link]); }
} Boudriga Imed 118
Les formulaires
Pour la soumission de formulaire, on ajoute un event binding (ngSubmit) et on ajoute un
bouton de type submit
<form [formGroup]="personneForm" (ngSubmit)='afficherTout()'>
<div>
Identifiant :
<input type="number" formControlName="id">
</div>
<div>
Nom :
<input type="text" formControlName="nom">
</div>
<div>
Pr´enom :
<input type="text" formControlName="prenom">
</div>
<button type='submit'>Envoyer</button>
</form>
Boudriga Imed 119
Les formulaires
Pour la validation de formulaire, on commence par désactiver le bouton tant que le
formulaire n'est pas valide
<form [formGroup]="personneForm" (ngSubmit)='afficherTout()'>
<div>
Identifiant :
<input type="number" formControlName="id">
</div>
<div>
Nom :
<input type="text" formControlName="nom">
</div>
<div>
Prénom :
<input type="text" formControlName="prenom">
</div>
<button type='submit' [disabled]='![Link]'>
Envoyer
</button>
</form>
Boudriga Imed 120
Les formulaires
Etape suivante : définir les règles de validation
• La classe FormControl peut prendre deux paramètres : la valeur initiale à afficher dans le
formulaire et la deuxième une règle de validation
• Pour définir une règle de validation, on peut utiliser la classe Angular Validators
contenant plusieurs règles de validation
Boudriga Imed 121
Les formulaires
Dans [Link], définissons quelques règles de validation
import { FormControl, FormGroup, Validators } from '@angular/forms';
personneForm = new FormGroup({
id: new FormControl('', [Link]),
nom: new FormControl('', [[Link](/ˆ[A-Z][a-z]{2,10}/),
[Link]]),
prenom : new FormControl('', [ [Link],
checkPrenomValidator ])
});
Explication
• Le champ id est obligatoire
• Le champ nom est obligatoire est doit respecter une expression régulière qui exige que la
première soit en majuscule est que le nombre de caractère soit entre 3 et 11.
• Le champ prenom est aussi obligatoire et doit respecter une fonction (qu'on a préparée
pour ça) appelée checkPrenomValidator
Boudriga Imed 122
Les formulaires
La fonction checkPrenomValidator()
function checkPrenomValidator(control: FormControl):
object {
const str: string = [Link];
if (str[0] >= 'A' && str[0] <= 'Z') {
return null;
} else {
return {erreur: 'Prénom non valide'};
}
}
Boudriga Imed 123
Les formulaires
Pour afficher les messages d'erreurs
<form [formGroup]="personneForm" (ngSubmit)='afficherTout()'>
<div>Identifiant : <input type="number" formControlName="id"> </div>
<div *ngIf="[Link] && ([Link] || [Link])">
<div *ngIf="[Link]?.required">L'identifiant est obligatoire</div> </div>
<div>Nom : <input type="text" formControlName="nom"></div>
<div *ngIf="[Link] && ([Link] || [Link])">
<div *ngIf="[Link]?.required">Le nom est obligatoire</div>
<div *ngIf="[Link]?.pattern">Première lettre en majuscule et min 3 lettres max
11</div></div>
<div>
Prénom :<input type="text" formControlName="prenom"> </div>
<div *ngIf="[Link] && ([Link] || [Link])">
<div *ngIf="[Link]?.required">Le prénom est obligatoire</div>
<div *ngIf="[Link]?.erreur">Première lettre en majuscule</div>
</div>
<button type='submit' [disabled]='![Link]'>Envoyer</button>
</form>
Boudriga Imed 124
Les formulaires
On ne peut accéder à la propriété invalid ni errors si on ne définit pas de getter pour
chaque FormControl dans [Link]
get nom(): AbstractControl {
return [Link]('nom');
}
get id(): AbstractControl {
return [Link]('id');
}
get prenom(): AbstractControl {
return [Link]('prenom');
}
Remarque
Il est possible d'imbriquer les FormGroup
Par exemple : un FormGroup adresse défini dans le FormGroup personne
Boudriga Imed 125
Les formulaires
Dans [Link], définissons les FormGroup imbriqués
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-form',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class FormComponent implements OnInit {
personneForm = new FormGroup({
id: new FormControl(''),
nom: new FormControl(''),
prenom: new FormControl(''),
adresse: new FormGroup({
rue: new FormControl(''),
ville: new FormControl(''),
codePostal: new FormControl('')
})
});
constructor() { }
ngOnInit() { }
afficherTout() { [Link]([Link]); }
} Boudriga Imed 126
Les formulaires
Dans [Link], créons le formulaire associé aux FormGroup imbriqués
<form [formGroup]="personneForm" (ngSubmit)='afficherTout()'>
<div>
Identifiant : <input type="number" formControlName="id">
</div>
<div>Nom : <input type="text" formControlName="nom"></div>
<div>Prénom : <input type="text" formControlName="prenom"></div>
<div formGroupName="adresse">
<h3>Adresse</h3>
<div>Rue : <input type="text" formControlName="rue"></div>
<div>
Ville : <input type="text" formControlName="ville">
</div>
<div>
Code postal : <input type="text" formControlName="codePostal">
</div></div>
<button type='submit'>Envoyer</button>
</form>
Boudriga Imed 127
Les formulaires
Remarque
• La méthode setValue() permet d'initialiser, ou modifier les valeurs de formulaire : il
faut préciser une valeur pour chaque FormControl du FormGroup
• La méthode patchValue() permet d'initialiser, ou modifier quelques (ou tous les)
FormControl du FormGroup
Exemple (dans ngOnInit())
[Link]({
prenom: 'imed',
adresse: {
codePostal: '4000'
}
});
Les champs Prénom et Code postal sont initialises avec les valeurs imed et 4000
Boudriga Imed 128
Les formulaires
FormBuilder
• Une classe service défini par Angular
• Donc, pour l'utiliser, il faut l'injecter dans le constructeur
• Il permet de simplifier la construction d'un formulaire en évitant les
répétitions de FormControl
Boudriga Imed 129
Les formulaires
Dans [Link], on injecte FormBuilder dans le constructeur puis on l'utilise
pour construire le formulaire
import { Component, OnInit } from '@angular/core';
import { Validators, FormBuilder } from '@angular/forms';
@Component({
selector: 'app-form',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class FormComponent implements OnInit {
personneForm = [Link]({
id: [null],
nom: ['boudriga'],
prenom: ['', [[Link], [Link](2)]],
adresse: [Link]({
rue: [''], ville: [''], codePostal: ['']
}), });
constructor(private fb: FormBuilder) { }
ngOnInit(): void { }
afficherTout(): void { [Link]([Link]); }
}
Boudriga Imed 130
Les formulaires
Dans [Link], rien à changer
<form [formGroup]="personneForm" (ngSubmit)='afficherTout()'>
<div>
Identifiant : <input type="number" formControlName="id">
</div>
<div>
Nom : <input type="text" formControlName="nom">
</div>
<div>Prénom : <input type="text" formControlName="prenom"></div>
<div formGroupName="adresse">
<h3>Adresse</h3>
<div>Rue : <input type="text" formControlName="rue"></div>
<div>Ville : <input type="text" formControlName="ville"></div>
<div>
Code postal : <input type="text" formControlName="codePostal">
</div>
</div>
<button [disabled]='![Link]'>Envoyer</button>
</form>
Boudriga Imed 131
Les formulaires
On peut aussi surveiller l'évolution de notre formulaire grâce à l'attribut status ( à placer
dans le formulaire)
<div>
état : {{ [Link] }}
</div>
FormArray
• Défini dans FormBuilder
• Il permet de définir un tableau de taille indéterminée de FormControl
• Une personne peut pratiquer plusieurs sports (le nombre peut varier d'une personne à
une autre) ) on peut utiliser FormArray
Boudriga Imed 132
Les formulaires
Dans [Link], définissons notre FormArray
personneForm = [Link]({
id: [null],
nom: ['boudriga'],
prenom: ['', [[Link], Validators.
minLength(2)]],
adresse: [Link]({
rue: [''],
ville: [''],
codePostal: ['']
}),
sports: [Link]([
[Link]('')
])
});
Pour afficher instantanément les sports ajoutés par l'utilisateur, on doit retourner notre
FormArray
get sports(): FormArray { return [Link]('sports') as FormArray;
}
Boudriga Imed 133
Les formulaires
Dans [Link], ajoutons notre FormArray à notre formulaire précédent
<div formArrayName="sports">
<h3>Sports </h3>
<button type=button (click)="ajouterSport()">
Ajouter sport
</button>
<div *ngFor="let sport of [Link]; let i=
index">
<div>
Sport :
<input type="text" [formControlName]="i">
</div>
</div>
</div>
Boudriga Imed 134
Les formulaires
Définissons maintenant la méthode ajouterSport()
ajouterSport(): void {
[Link]([Link](''));
}
Remarque
• On ajoute à notre tableau un nouvel élément vide pour que l'utilisateur puisse saisir
un nouveau sport.
• Le sport ajouté par l'utilisateur est lié directement à notre FormArray
• FormArray est une classe qui peut être aussi utilisée dans un FormGroup
Boudriga Imed 135
Boudriga Imed 136
Les requêtes HTTP
Le protocole HTTP
• Hypertext Transfer Protocol
• Protocole de communication entre client et serveur
• Basé sur la notion requête - réponse appelée généralement (HTTP Request -
HTTP Response)
• Plusieurs types de requetés = méthodes HTTP
GET
POST
DELETE
PUT
• Toutes ces méthodes prennent en paramètre l'adresse du serveur ( pour certaines
méthodes les données à manipuler)
Boudriga Imed 137
Les requêtes HTTP
RESTful, REST et HTTP, c'est quoi le lien?
• Le Web et l'API REST sont basés sur le protocole HTTP (architecture client/serveur)
• RESTful est juste un adjectif qui désigne une API REST.
• L'API REST utilise donc des méthodes comme get, post, delete... pour l'échange de
données entre client et serveur (Un client envoie une requête, et le serveur retourne
une réponse)
Boudriga Imed 138
Les requêtes HTTP
Angular et les serveurs
• Angular est un Framework JavaScript qui permet de réaliser des applications web qui
s'exécutent coté client
• Angular possède un module, HttpModule (HttpClientModule depuis la version 5)
facilitant la réalisation de requête HTTP vers n'importe quel serveur via les classes
suivantes :
HttpClient
HttpHeaders
HttpInterceptor
HttpRequest
...
• Pour le coté serveur : on peut utiliser un serveur JSON qui recevra des requetés HTTP
envoyées par Angular et retourner une réponse
Boudriga Imed 139
Les requêtes HTTP
json-server
• Un serveur Http de test, open-source, pour les développeurs Front-End
• Documentation :
[Link]
Pour l'installer
npm install -g json-server
Boudriga Imed 140
Les requêtes HTTP
Créons un fichier [Link] qui va nous servir de base de données et qui sera situé
au même niveau que le dossier du notre projet Angular8
{
"personnes": [
{
"nom": "Boudriga",
"prenom": "imed",
"id": 1
},
{
"nom": "Saleh",
"prenom": "ben saleh",
"id": 2
}
]
}
Boudriga Imed 141
Les requêtes HTTP
Avant d'envoyer des requetés HTTP au json-server
Utiliser la console pour se placer dans le répertoire contenant le fichier [Link]
Démarrer json-server sur le port 5555 en précisant le nom de notre base de donnéesH &
Lancer le serveur
json-server -p 5555 [Link]
Résultat (parmi toutes les lignes affichées)
Resources
[Link]
Boudriga Imed 142
Les requêtes HTTP
[Link]
• C'est l'URL qui sera utilisée par le client pour réaliser des requetés HTTP
• Si on copie cette adresse et qu'on la colle dans le navigateur, on obtient toutes les
données de notre base de données
Boudriga Imed 143
Les requêtes HTTP
Explication
• Les données sont saisies (ou affichées) dans le template .[Link]
• La classe .[Link] peut récupérer des données dont la source est le template
.[Link] pour les passer au service ou récupérer des données dont la source
est le service pour les passer au template .[Link].
• En faisant une injection de dépendance du service .[Link] dans .[Link], ce
dernier peut l'utiliser pour persister ou récupérer des données
• En faisant une injection de dépendance de la classe HttpClient dans .[Link], ce
dernier peut effectuer des requetés HTTP en précisant chaque fois la méthode et l'URL.
Boudriga Imed 144
Les requêtes HTTP
Considérons le service [Link]
import { Injectable } from '@angular/core';
import { Personne } from '../interfaces/personne';
@Injectable({
providedIn: 'root'
})
export class PersonneService {
url = '[Link]
constructor() {
}
getAll(){
}
addPerson(p: Personne){
}
}
Boudriga Imed 145
Les requêtes HTTP
Dans le constructeur de la classe PersonneComponent, on injecte le service PersonneService
import { Component, OnInit } from '@angular/core';
import { Personne } from '../../interfaces/personne';
import { PersonneService } from '../../services/[Link]';
@Component({
selector: 'app-personne',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class PersonneComponent implements OnInit {
personne: Personne = {};
personnes: Array <Personne> = [];
constructor(private personneService: PersonneService) { }
ngOnInit() {
[Link] = [Link]();
}
}
Boudriga Imed 146
Les requêtes HTTP
Le template [Link] où on affiche le résultat
<ul>
<li *ngFor="let elt of personnes">
{{ [Link] }} {{ [Link] }}
</li>
</ul>
Boudriga Imed 147
Les requêtes HTTP
Commençons par injecter le service HttpClient dans [Link]
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Personne } from '../interfaces/personne';
@Injectable({
providedIn: 'root'
})
export class PersonneService {
url = '[Link]
constructor(private http: HttpClient) {
}
getAll(){
}
addPerson(p: Personne){
}
}
Boudriga Imed 148
Les requêtes HTTP
Il faut importer le module HttpClientModule dans [Link] pour utiliser HttpClient
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
//+ les autres imports
@NgModule({
declarations: [
AppComponent,
AdresseComponent,
PersonneComponent,
FormulaireComponent
],
imports: [
BrowserModule,
FormsModule,
HttpClientModule
],
providers: [PersonneService],
bootstrap: [AppComponent]
})
export class AppModule { }Boudriga Imed 149
Les requêtes HTTP
Faisons une première requête HTTP pour récupérer la liste des personnes
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Personne } from '../interfaces/personne';
@Injectable({
providedIn: 'root'
})
export class PersonneService {
url:string= "[Link]
constructor(private http: HttpClient) {
}
getAll(): Observable<Array<Personne>> {
return [Link]<Array<Personne>>([Link]);
}
addPerson(p: Personne) {
}
}
Boudriga Imed 150
Les requêtes HTTP
Pour récupérer la liste des personnes dans [Link], il faut s'abonner
import { Component, OnInit } from '@angular/core';
import { Personne } from '../../interfaces/personne';
import { PersonneService } from '../../services/[Link]';
@Component({
selector: 'app-personne',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class PersonneComponent implements OnInit {
personne: Personne = {};
personnes: Array <Personne> = [];
constructor(private personneService: PersonneService) { }
ngOnInit(): void {
[Link]().subscribe(res => {
[Link] = res;
});
} En lançant l'application, la liste de personnes est affichée.
}
Boudriga Imed 151
Les requêtes HTTP
Récupérons le formulaire permettant d'ajouter une personne (voir chapitre formulaire)
<form #monForm=ngForm (ngSubmit)=ajouterPersonne()>
<div>
Nom : <input type=text name=nom [(ngModel)]=[Link] required #
nom="ngModel">
</div>
<div [hidden]="[Link] || [Link]">
Le nom est obligatoire
</div><div>
Prénom : <input type=text name=prenom [(ngModel)]=[Link] required
#prenom="ngModel">
</div>
<div [hidden]="[Link] || [Link]">
Le prénom est obligatoire
</div>
<div>
<button [disabled]=![Link]>
ajouter
</button></div></form>
<!-- garder la partie permettant d'afficher les personnes -->
Boudriga Imed 152
Les requêtes HTTP
Récupérons aussi les propriétés pour les classes CSS fournies par Angular
.ng-invalid:not(form){
border-left: 5px solid red;
}
.ng-valid:not(form){
border-left: 5px solid green;
}
Boudriga Imed 153
Les requêtes HTTP
Préparons la requête HTTP permettant d'ajouter une personne
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Personne } from '../interfaces/personne';
@Injectable({
providedIn: 'root'
})
export class PersonneService {
url = '[Link]
constructor(private http: HttpClient) { }
getAll(): Observable<Array<Personne>> {
return [Link]<Array<Personne>>([Link]);
}
addPerson(p: Personne): Observable<Personne> {
return [Link]([Link], p);
}
}
Boudriga Imed 154
Les requêtes HTTP
Ajoutons la méthode ajouterPersonne() dans [Link]
import { Component, OnInit } from '@angular/core';
import { Personne } from '../../interfaces/personne';
import { PersonneService } from '../../services/[Link]';
@Component({
selector: 'app-personne',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class PersonneComponent implements OnInit {
personne: Personne = {};
personnes: Array<Personne> = [];
constructor(private personneService: PersonneService) { }
ngOnInit() {
[Link]().subscribe(res => {
[Link] = res;
}); }
ajouterPersonne() {
[Link]([Link]).subscribe(res => {
[Link](res);
}); } }
Boudriga Imed 155
Les requêtes HTTP
Si on essaye d'ajouter une personne
La personne est ajoutée dans le fichier [Link]
Mais elle n'est pas affichée dans la page qu'après l'actualisation car on ne met pas à jour la
liste des personnes
Deux solutions possibles
• Soit on ajoute la personne au tableau personnes de la classe [Link]
lorsqu'on l'ajoute dans notre fichier [Link]
• Soit on refait appel à la méthode getAll() de notre service [Link] dans
[Link] lorsqu'on l'ajoute dans notre fichier [Link] pour mettre
à jour le tableau personnes
Boudriga Imed 156
Les requêtes HTTP
Modifions la méthode ajouterPersonne() dans [Link]
// les imports
@Component({
selector: 'app-personne',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class PersonneComponent implements OnInit {
personne: Personne = {};
personnes: Array <Personne> = [];
constructor(private personneService: PersonneService) { }
ngOnInit() {
[Link]().subscribe(res => {
[Link] = res;
});
}
ajouterPersonne() {
[Link]([Link]).subscribe(res => {
[Link]().subscribe(result => {
[Link] = result;
});
}); } } Boudriga Imed 157
Les requêtes HTTP
Question : que contient la variable res de l'exemple précédent
Elle contient l'objet qu'on a ajouté dans la base de données avec une valeur attribuée à id (et
qu'on peut utiliser pour faire d'autres opérations)
Exercice
Ajouter du code HTML dans [Link], des méthodes dans
[Link] et des méthodes dans [Link] (avec les méthodes HTTP
delete et put) pour permettre de modifier et de supprimer des personnes.
Boudriga Imed 158
Les requêtes HTTP
Le répertoire environments
• contient deux fichiers permettant de définir les variables d'environnement d'une
application Angular :
- [Link] pour le mode développement
- [Link] pour le mode production
• Angular remplacera automatiquement le premier fichier par le deuxième fichier à chaque
fois exécution de ng build --prod.
Contenu initial de [Link]
export const environment = {
production: false,
};
Ajoutons dans [Link] l'adresse le préfixe de nos API REST
export const environment = {
production: false,
APIEndpoint: '[Link]
};
Boudriga Imed 159
Les requêtes HTTP
Modifions [Link] et utilisons la variable ajoutée dans [Link]
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Personne } from '../interfaces/personne';
import { environment } from '../../environments/environment';
@Injectable({
providedIn: 'root'
})
export class PersonneService {
url: string;
constructor(private http: HttpClient) {
const APIEndpoint = [Link];
[Link] = APIEndpoint + 'personnes/';
}
getAll() {
return [Link]<Array<Personne>>([Link]);
}
addPerson(p: Personne) {
return [Link]([Link], p);
}
}
Boudriga Imed 160
Les requêtes HTTP
Modifions [Link] et utilisons la variable ajoutée dans [Link]
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Personne } from '../interfaces/personne';
import { environment } from '../../environments/environment';
@Injectable({
providedIn: 'root'
})
export class PersonneService {
url: string;
constructor(private http: HttpClient) {
const APIEndpoint = [Link];
[Link] = APIEndpoint + 'personnes/';
}
getAll() {
return [Link]<Array<Personne>>([Link]);
}
addPerson(p: Personne) {
return [Link]([Link], p);
}
}
Boudriga Imed 161
Boudriga Imed 162
Routage Angular
Pour créer un nouveau composant
ng generate component component-name
Ou utiliser le raccourci
ng g c component-name
Exemple : créons trois composants adresse, stagiaire et menu dans un répertoire
composants
ng g c composants/adresse
ng g c composants/stagiaire
ng g c composants/menu
Boudriga Imed 163
Routage Angular
Résultat
CREATE src/app/composants/adresse/[Link] (22 bytes)
CREATE src/app/composants/adresse/[Link] (635 bytes)
CREATE src/app/composants/adresse/[Link] (273 bytes)
CREATE src/app/composants/adresse/[Link] (0 bytes)
UPDATE src/app/[Link] (490 bytes)
CREATE src/app/composants/stagiaire/[Link] (23 bytes)
CREATE src/app/composants/stagiaire/[Link] (642bytes)
CREATE src/app/composants/stagiaire/[Link] (277 bytes)
CREATE src/app/composants/stagiaire/[Link] (0 bytes)
UPDATE src/app/[Link] (591 bytes)
CREATE src/app/composants/menu/[Link] (19 bytes)
CREATE src/app/composants/menu/[Link] (614 bytes)
CREATE src/app/composants/menu/[Link] (267 bytes)
CREATE src/app/composants/menu/[Link] (0 bytes)
UPDATE src/app/[Link] (1044 bytes)
Boudriga Imed 164
Routage Angular
Quatre fichiers crées pour chaque composant
[Link] , [Link] , [Link] et [Link]
avec x = stagiaire, adresse ou menu
Trois déclarations effectuées dans la section déclarations de [Link]
@NgModule({
declarations: [
AppComponent,
ObservableComponent,
AdresseComponent,
StagiaireComponent,
MenuComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Boudriga Imed 165
Routage Angular
Pour afficher le contenu de ces trois composants dans [Link]
<app-stagiaire></app-stagiaire>
<app-adresse></app-adresse>
<app-menu></app-menu>
• En général, on préfère ne pas afficher tous les composants dans le composant principal
• On associe plutôt un chemin à chaque composant
• Le composant sera affiché dans le composant principal si son chemin apparaît dans l'URL
de la requête HTTP
Boudriga Imed 166
Routage Angular
Module de routage
À la création du projet, on a demandé la génération d'un fichier de routage :
[Link]
• Ce fichier permet d'assurer le mapping chemin/composant
• Il contient un tableau vide de type Routes
• Chaque route peut avoir comme attributs (path, component, redirectTo, children...)
Contenu de [Link]
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [];
@NgModule({
imports: [[Link](routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Boudriga Imed 167
Routage Angular
RouterModule a deux méthode statiques qui prennent en paramètre un tableau de Routes :
• .forRoot(tableau) : pour le module principal (racine)
• .forChild(tableau) : pour les sous-modules inclus dans le module principal
Définissons des routes dans ce tableau de routes de [Link]
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { StagiaireComponent } from './composants/stagiaire/stagiaire.
component';
import { AdresseComponent } from './composants/adresse/adresse.
component';
const routes: Routes = [
{ path: 'stagiaire', component: StagiaireComponent },
{ path: 'adresse', component: AdresseComponent },
];
@NgModule({
imports: [[Link](routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Pas de route pour le composant menu car on veut l'afficher quelle que soit le chemin demandé.
Boudriga Imed 168
Routage Angular
Remarques
• L'URL saisies auront la forme suivante localhost:4200/adresse
ou localhost:4200/stagiaire
• Faut-il ajouter / comme préfix aux valeurs de l'attribut path?
• Non, car / a été défini dans [Link] dans la balise <base href="/">
Boudriga Imed 169
Routage Angular
{ enableTracing: true } permet de garder une trace de la recherche d'un chemin (pour
le débogage).
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { StagiaireComponent } from './composants/stagiaire/stagiaire.
component';
import { AdresseComponent } from './composants/adresse/adresse.
component';
const routes: Routes = [
{ path: 'stagiaire', component: StagiaireComponent },
{ path: 'adresse', component: AdresseComponent },
];
@NgModule({
imports: [[Link](routes, { enableTracing: true })],
exports: [RouterModule]
})
export class AppRoutingModule { }
Boudriga Imed 170
Routage Angular
Comment faire pour tester?
Il faut saisir localhost:4200/adresse ou localhost:4200/stagiaire
dans la barre d'adresse du navigateur
Où le composant sera affiché dans [Link]?
Il faut lui indiquer l'emplacement mais pas en ajoutant le sélecteur du composant
Il faut indiquer l'emplacement du composant à charger dans [Link] en
ajoutant la balise suivante
[Link]
app-header
<router-outlet></router-outlet>
app-footer
Boudriga Imed 171
Routage Angular
Remarques
• Pour accéder à un composant, l'utilisateur doit connaître son chemin défini dans le
tableau de routes (or ceci n'est pas vraiment très pratique)
• On peut plutôt définir un menu contenant des liens vers nos différents composants
Commençons par définir le menu suivant dans [Link]
<ul>
<li><a href=''> Accueil </a></li>
<li><a href='stagiaire'> Stagiaire </a></li>
<li><a href='adresse'> Adresse </a></li>
</ul>
Dans [Link], on ajoute le menu et on indique l'emplacement des
composants à afficher
<app-menu></app-menu>
<router-outlet></router-outlet>
Remarque
Chaque fois qu'on clique sur un lien la page est rechargée : ce n'est pas le but d'une
application mono-page
Boudriga Imed 172
Routage Angular
Solution : remplacer l'attribut href par routerLink
<ul>
<li><a routerLink=''> Accueil </a></li>
<li><a routerLink='stagiaire'> Stagiaire </a></li>
<li><a routerLink='adresse'> Adresse </a></li>
</ul>
Pour afficher la route active en gras
<ul>
<li><a routerLink=''> Accueil </a></li>
<li routerLinkActive=active>
<a routerLink='stagiaire'> Stagiaire </a>
</li>
<li routerLinkActive=active>
<a routerLink='adresse'> Adresse </a>
</li>
</ul>
Dans [Link], il faut définir la classe active
.active {
font-weight: bold;
} Boudriga Imed 173
Routage Angular
Si on ajoute routerLinkActive=active, il sera en gras quelle que soit la page visitée, pour cela
on ajoute [routerLinkActiveOptions]={ exact: true }" pour que la classe soit uniquement
ajoutée lorsque la route correspond exactement à la valeur de routerLink
<ul>
<li>
<a routerLink='' routerLinkActive=active [
routerLinkActiveOptions]="{ exact: true }"> Accueil </a>
</li>
<li routerLinkActive=active>
<a routerLink='stagiaire'> Stagiaire </a>
</li>
<li routerLinkActive=active>
<a routerLink='adresse'> Adresse </a>
</li>
</ul>
Boudriga Imed 174
Routage Angular
Pour créer un module de routage (Si on n'a pas accepté qu'il soit généré à la création du
projet)
ng generate module app-routing --flat --module=app
Comprenons la commande
• ng generate module app-routing : pour générer un module de routage appelé
app-routing.
• --flat : pour placer le fichier dans src/app et éviter de créer un répertoire propre à ce
module.
--module=app : pour enregistrer ce module dans le tableau imports de AppModule.
Boudriga Imed 175
Routage Angular
La section imports du fichier [Link]
imports: [
BrowserModule,
[Link]([
{ path: 'stagiaire', component: StagiaireComponent },
{ path: 'adresse', component: AdresseComponent },
]),
AppRoutingModule
],
Le fichier [Link]
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({
imports: [
CommonModule
],
declarations: []
})
export class AppRoutingModule { }
Boudriga Imed 176
Routage Angular
La section imports du fichier [Link]
imports: [
BrowserModule,
AppRoutingModule
],
Modifions le contenu du fichier [Link]
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
// + les autres imports de components
const routes: Routes = [
{ path: 'stagiaire', component: StagiaireComponent },
{ path: 'adresse', component: AdresseComponent }
];
@NgModule({
imports: [[Link](routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Boudriga Imed 177
Routage Angular
Pour activer le traçage
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
// + les autres imports de components
const routes: Routes = [
{ path: 'stagiaire', component: StagiaireComponent },
{ path: 'adresse', component: AdresseComponent }
];
@NgModule({
imports: [[Link](routes, { enableTracing: true
})],
exports: [RouterModule]
})
export class AppRoutingModule { }
Boudriga Imed 178
Routage Angular
Deux formes de paramètres de requête
• /chemin/param1/param2
• /chemin?var1=value1&var2=value2
Pour ces deux formes de paramètres
• Deux manières différentes de définir les routes
• Deux objets différents permettant de récupérer les valeurs respectives
✓ paramMap pour /chemin/param1/param2
✓ queryParamMap pour /chemin?var1=value1&var2=value2
Boudriga Imed 179
Routage Angular
Définissons une route de la forme /chemin/param1/param2 dans [Link]
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { StagiaireComponent } from './composants/stagiaire/stagiaire.
component';
import { AdresseComponent } from './composants/adresse/adresse.
component';
const routes: Routes = [
{ path: 'stagiaire', component: StagiaireComponent },
{ path: 'stagiaire/:nom/:prenom', component: StagiaireComponent },
{ path: 'adresse', component: AdresseComponent },
];
@NgModule({
imports: [[Link](routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Boudriga Imed 180
Routage Angular
Pour récupérer les paramètres d'une route de la forme stagiaire/:nom/:prenom, il faut :
• aller dans le composant concerné (ici, [Link])
• faire une injection de dépendance de la classe ActivatedRoute (comme paramètre de
constructeur)
• utiliser un objet cette classe dans la méthode ngOnInit()
✓ soit par l'intermédiaire d'un objet paramMap pour récupérer les
paramètres (solution avec les observables (asynchrone))
✓ soit par l'intermédiaire d'un objet params pour récupérer les
paramètres (solution avec les snapshot (instantanée))
Boudriga Imed 181
Routage Angular
Pour récupérer les paramètres d'une route de la forme stagiaire/:nom/:prenom, dans
[Link]
import { ActivatedRoute } from '@angular/router';
// + les autres imports de components
@Component({
selector: 'app-stagiaire',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class StagiaireComponent implements OnInit {
nom: any;
prenom: any;
constructor(private route: ActivatedRoute) { }
ngOnInit() {
[Link](res => {
[Link] = [Link]('nom');
[Link] = [Link]('prenom');
[Link]([Link] + ' ' + [Link]);
});
}
}
Boudriga Imed 182
Routage Angular
Dans [Link]
<h2>Stagiaire</h2>
<p> Bonjour {{ prenom }} {{ nom }} </p>
constructor et ngOnInit
• constructor : fonction JavaScript qui sert à initialiser les attributs d'une classe
• constructor avec Angular sert seulement à faire les injections de dépendances
• ngOnInit : méthode exécutée quand Angular a fini d'initialiser le composant (charger
@Input()...)
Boudriga Imed 183
Routage Angular
La deuxième solution avec snapshot
import { ActivatedRoute } from '@angular/router';
// + les autres imports de components
@Component({
selector: 'app-stagiaire',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class StagiaireComponent implements OnInit {
nom: any;
prenom: any;
constructor(private route: ActivatedRoute) { }
ngOnInit() {
[Link] = [Link];
[Link] = [Link];
[Link]([Link] + ' ' + [Link]);
}
}
Boudriga Imed 184
Routage Angular
Pour récupérer les paramètres d'une route de la forme
adresse?ville=value1&rue=value2&codepostal=value3, il faut :
• aller dans le composant concerné (ici, [Link])
• faire une injection de dépendance de la classe ActivatedRoute
• utiliser un objet cette classe dans la méthode ngOnInit()
✓ soit par l'intermédiaire d'un objet queryParamMap pour récupérer les
paramètres (solution avec les observables)
✓ soit par l'intermédiaire d'un objet queryParams pour récupérer les paramètres
(solution avec les snapshot)
Boudriga Imed 185
Routage Angular
Pas besoin de définir une route pour récupérer les paramètres rue, codepostal et ville
import { ActivatedRoute } from '@angular/router';
// + les autres imports de components
@Component({
selector: 'app-adresse',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class AdresseComponent implements OnInit {
rue = '';
codePostal = '';
ville = '';
constructor(private route: ActivatedRoute) { }
ngOnInit(): void {
[Link](
res => {
[Link] = [Link]('ville');
[Link] = [Link]('rue');
[Link] = [Link]('codepostal');
} ); } }
Boudriga Imed 186
Routage Angular
Dans [Link]
<h2>Adresse</h2>
<ul>
<li>Rue : {{ rue }} </li>
<li>Code Postal : {{ codePostal }} </li>
<li>Ville : {{ ville }} </li>
</ul>
Boudriga Imed 187
Routage Angular
La deuxième solution avec snapshot
import { ActivatedRoute } from '@angular/router';
// + les autres imports de components
@Component({
selector: 'app-adresse',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class AdresseComponent implements OnInit {
rue = '';
codePostal = '';
ville = '';
constructor(private route: ActivatedRoute) { }
ngOnInit(): void {
[Link] = [Link];
[Link] = [Link];
[Link] = [Link];
}
}
Boudriga Imed 188
Routage Angular
Une première méthode classique en HTML
<ul>
<li><a routerLink=''> Accueil </a></li>
<li>
<a routerLink='{{ lienStagiaire }}'> Stagiaire </a>
</li>
<li><a routerLink='/adresse'> Adresse </a></li>
</ul>
On comprend de {{ lienStagiaire }} qu'il existe un attribut lienStagiaire dans
[Link]
Boudriga Imed 189
Routage Angular
Dans [Link]
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-menu',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class MenuComponent implements OnInit {
lienStagiaire = '';
param1 = 'imed';
param2 = 'boudriga';
constructor() {
[Link] = '/stagiaire/' + this.param1 + '/' + this.
param2;
}
ngOnInit(): void {
}
}
Boudriga Imed 190
Routage Angular
Une deuxième écriture avec le one way binding (property binding)
<ul>
<li><a routerLink=''> Accueil </a> </li>
<li><a [routerLink]='lienStagiaire'> Stagiaire </a
></li>
<li><a routerLink='/adresse'> Adresse </a></li>
</ul>
Une troisième écriture
<ul>
<li>
<a routerLink=''> Accueil </a>
</li>
<li>
<a [routerLink]="['/stagiaire',param1,param2]">
Stagiaire </a>
</li>
<li>
<a routerLink='/adresse'> Adresse </a>
</li>
Boudriga Imed 191
</ul>
Routage Angular
Pour construire un chemin de la forme /chemin?param1=value1¶m2=value2
<ul>
<li>
<a routerLink=''> Accueil </a>
</li>
<li>
<a [routerLink]="['/stagiaire',param1,param2]"> Stagiaire </a>
</li>
<li>
<a [routerLink]="['/adresse']" [queryParams]="{ ville: 'Sousse',
codepostal: '4000', rue: 'liberte'}">
Adresse
</a>
</li>
</ul>
Boudriga Imed 192
Routage Angular
Exercice
• Dans [Link], construisez un lien vers la route /stagiaire avec deux
paramètres
• Dans [Link], utilisez la solution snapshot puis observable pour récupérer
les paramètres. Dans [Link], on affiche les paramètres.
• Vérifier, en cliquant sur le lien, que les nouveaux paramètres sont affichés
Conclusion
• Si la valeur initiale de paramètre est utilisée seulement à l'initialisation du composant et
ne risque pas de changer, utilisez les snapshot.
• Si la route risque de changer tout en restant dans le même composant, utilisez les
observables. L'initialisation du composant (ngOnInit()) ne serait donc pas appelée à
nouveau, l'observateur sera notifié lorsque l'URL a été modifiée.
Boudriga Imed 193
Routage Angular
Dans [Link]
import { Router } from '@angular/router';
// + les autres imports de components
@Component({
selector: 'app-adresse',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class AdresseComponent implements OnInit {
nom = 'boudriga';
prenom = 'imed';
constructor(private router: Router) { }
goToStagiaire(): void {
[Link]('/stagiaire/' + [Link] + '/' + this.
prenom);
}
}
En appelant la méthode goToStagiaire(), on sera redirigé vers /stagiaire/imed/boudriga.
Boudriga Imed 194
Routage Angular
On peut aussi utiliser la méthode navigate()
import { Router } from '@angular/router';
// + les autres imports de components
@Component({
selector: 'app-adresse',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class AdresseComponent implements OnInit {
nom = 'boudriga';
prenom = 'imed';
constructor(private router: Router) { }
goToStagiaire() {
[Link](['/stagiaire', [Link] , [Link]]);
}
}
Boudriga Imed 195
Routage Angular
On peut rediriger vers un chemin existant
On peut créer un chemin vide pour que l'URL localhost:4200 soit accessible
const routes: Routes = [
{ path: 'stagiaire/:param1/:param2', component:StagiaireComponent },
{ path: 'stagiaire', component: StagiaireComponent },
{ path: 'adresse', component: AdresseComponent },
{ path: 'trainee', redirectTo: '/stagiaire' },
{ path: '', redirectTo: '/stagiaire', pathMatch: 'full' }
];
Remarque
• Sans la partie pathMatch: 'full' (pour les chemins vides), toutes les routes déclarées après
cette dernière ne seront pas accessibles.
• pathMatch: 'full' ne laisse donc passer que les requêtes dont le chemin correspond
exactement au chemin vide
• La deuxième valeur possible pour pathMatch est 'prefix'
Boudriga Imed 196
Routage Angular
On peut créer un composant error et l'afficher en cas de chemin inexistant
const routes: Routes = [
{ path: 'stagiaire', component: StagiaireComponent },
{ path: 'stagiaire/:nom/:prenom', component: StagiaireComponent },
{ path: 'stagiaire', component: StagiaireComponent },
{ path: 'adresse', component: AdresseComponent },
{ path: 'trainee', redirectTo: '/stagiaire' },
{ path: 'error', component: ErrorComponent },
{ path: '', redirectTo: '/stagiaire', pathMatch: 'full'},
{ path: '**', redirectTo: '/error' }
];
Le chemin ** doit être le dernier. Autrement, toutes les requêtes seront redirigées
vers le composant error.
Boudriga Imed 197
Boudriga Imed 198
sous-module Angular
Pour créer un nouveau sous-module
ng generate module module-name
Ou utiliser le raccourci
ng g m module-name
Pour créer un sous-module vehicule (ce dernier ne sera pas enregistré dans [Link])
ng g m vehicule
Boudriga Imed 199
sous-module Angular
Pour créer un sous-module vehicule et l'enregistrer dans [Link]
ng g m vehicule --module=app
Pour créer un sous-module vehicule, l'enregistrer dans [Link] et créer son
module de routage (l'ordre des options n'a pas d'importance)
ng g m vehicule --module=app –routing
Pour bien structurer l'application, on regroupe les modules dans un dossier modules
ng g m modules/vehicule --module=app --routing
Le resultat est :
CREATE src/app/modules/vehicule/[Link] (252 bytes)
CREATE src/app/modules/vehicule/[Link] (288 bytes)
UPDATE src/app/[Link] (680 bytes)
Boudriga Imed 200
sous-module Angular
• Deux fichiers créés :
✓ [Link]
✓ [Link]
• Une mise à jour effectuée : enregistrement de ce sous-module dans [Link]
• Si l'option --module=app n'a pas été précisée, il faut déclarer [Link] dans
[Link]
Boudriga Imed 201
sous-module Angular
Pour déclarer [Link] dans [Link]
import { VehiculeModule } from './vehicule/[Link]';
// + les autres imports
@NgModule({
declarations: [
AppComponent,
StagiaireComponent,
AdresseComponent
],
imports: [
BrowserModule,
AppRoutingModule,
VehiculeModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Boudriga Imed 202
sous-module Angular
Deux méthodes différentes pour la création de nouveaux composants dans un sous-module
• Se placer dans le sous-module et créer le nouveau composant
• Préciser le nom du module au moment de la création du composant
Exemple avec la première méthode cd src/app/modules/vehicule
ng g c voiture
Le résultat est
CREATE src/app/modules/vehicule/voiture/[Link] (22 bytes)
CREATE src/app/modules/vehicule/voiture/[Link] (635 bytes)
CREATE src/app/modules/vehicule/voiture/[Link] (273 bytes)
CREATE src/app/modules/vehicule/voiture/[Link] (0 bytes)
UPDATE src/app/modules/vehicule/[Link] (446 bytes)
Exemple avec la deuxième méthode ng g c modules/vehicule/camion
Le résultat est
CREATE src/app/modules/vehicule/camion/[Link] (21 bytes)
CREATE src/app/modules/vehicule/camion/[Link] (628 bytes)
CREATE src/app/modules/vehicule/camion/[Link] (269 bytes)
CREATE src/app/modules/vehicule/camion/[Link] (0 bytes)
UPDATE src/app/modules/vehicule/[Link] (364 bytes)
Boudriga Imed 203
sous-module Angular
Remarque
Les deux mises à jour effectuées correspondent à la déclaration de ces deux composants
dans [Link]
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { VehiculeRoutingModule } from './[Link]';
import { CamionComponent } from './camion/[Link]';
import { VoitureComponent } from './voiture/[Link]';
@NgModule({
declarations: [CamionComponent, VoitureComponent],
imports: [
CommonModule,
VehiculeRoutingModule
]
})
export class VehiculeModule { }
Boudriga Imed 204
sous-module Angular
Question 1 : pourquoi CommonModule?
C'est le module contenant les pipes et les directives Angular
Question 2 : CommonModule n'est pas importé dans [Link], pourquoi?
Dans [Link], on importe BrowserModule et ce dernier importe CommonModule
Commençons par créer un module de routage pour le sous-module vehicule (si on n'a pas
ajouté l'option --routing à la création) :
ng generate module vehicule/vehicule-routing --flat --module=vehicule
Explication
• vehicule/vehicule-routing : le module de routage appelé vehicule-routing sera créé dans
le répertoire du module vehicule
• --flat pour ne pas créer un répertoire vehicule-routing
• --module=vehicule pour déclarer le module créé dans [Link]
Boudriga Imed 205
sous-module Angular
Deux solutions
• Définir les routes dans [Link]
• Définir une base pour le module dans [Link] et une route pour chaque
composant de ce module dans [Link]
Boudriga Imed 206
sous-module Angular
Dans [Link], définissons les routes /vehicule/camion, /vehicule/voiture et /vehicule
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
// + les autres imports de components
const routes: Routes = [
{ path: 'stagiaire', component: StagiaireComponent },
{ path: 'stagiaire/:nom/:prenom', component: StagiaireComponent },
{ path: 'stagiaire', component: StagiaireComponent },
{ path: 'adresse', component: AdresseComponent },
{ path: 'trainee', redirectTo: '/stagiaire' },
{ path: 'error', component: ErrorComponent },
{
path: 'vehicule', children: [
{ path: 'camion', component: CamionComponent },
{ path: 'voiture', component: VoitureComponent },
{ path: '', component: VoitureComponent }
]
},
{ path: '', redirectTo: '/stagiaire', pathMatch: 'full' },
{ path: '**', redirectTo: '/error' }
];
@NgModule({
imports: [[Link](routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Boudriga Imed 207
sous-module Angular
Rien à ajouter dans [Link]
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [];
@NgModule({
imports: [[Link](routes)],
exports: [RouterModule]
})
export class VehiculeRoutingModule { }
Ajoutons le constructeur suivant dans [Link]
export class VehiculeModule {
constructor() { [Link]('vehicule-module'); }
}
Ajoutons le constructeur suivant dans [Link]
export class AppModule {
constructor() { [Link]('app-module'); }
}
Boudriga Imed 208
sous-module Angular
Explication
• Allez sur /vehicule/camion et /vehicule/voiture et vérifiez que leurs composants
respectifs s'affichent
• Allez aussi les routes précédentes (/adresse par exemple) et vérifier que dans tous les cas
les deux messages app-module et vehicule-module sont affichés dans la console.
Boudriga Imed 209
sous-module Angular
Deuxième solution : dans [Link], commentons la dernière partie ajoutée et
les deux dernières routes
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
// + les autres imports de components
const routes: Routes = [
{ path: 'stagiaire', component: StagiaireComponent },
{ path: 'stagiaire/:nom/:prenom', component: StagiaireComponent },
{ path: 'stagiaire', component: StagiaireComponent },
{ path: 'adresse', component: AdresseComponent },
{ path: 'trainee', redirectTo: '/stagiaire' },
{ path: 'error', component: ErrorComponent },
// {
// path: 'vehicule', children: [
// { path: 'camion', component: CamionComponent },
// { path: 'voiture', component: VoitureComponent },
// { path: '', component: VoitureComponent }
// ]
// },
// { path: '', redirectTo: '/stagiaire', pathMatch: 'full' },
// { path: '**', redirectTo: '/error' }
];
@NgModule({
imports: [[Link](routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Boudriga Imed 210
sous-module Angular
Ajoutons le routage dans [Link]
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { VoitureComponent } from './voiture/[Link]';
import { CamionComponent } from './camion/[Link]';
const routes: Routes = [
{ path: 'camion', component: CamionComponent },
{ path: 'voiture', component: VoitureComponent },
];
@NgModule({
imports: [[Link](routes)],
exports: [RouterModule]
})
export class VehiculeRoutingModule { }
Ainsi, nos chemins sont /voiture et /camion.
Boudriga Imed 211
sous-module Angular
Lazy loading
• On doit définir la base dans [Link]
• Faire référence à [Link] avec la clé loadChildren
Boudriga Imed 212
sous-module Angular
Modifions le contenu du fichier [Link] (solution utilisée jusqu'`a la
version 8 d'Angular)
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router’;
import {VehiculeModule } from './modules/vehicule/[Link]’;
// + les autres imports de components
const routes: Routes = [
{ path: 'stagiaire', component: StagiaireComponent },
{ path: 'stagiaire/:nom/:prenom', component: StagiaireComponent },
{ path: 'stagiaire', component: StagiaireComponent },
{ path: 'adresse', component: AdresseComponent },
{ path: 'trainee', redirectTo: '/stagiaire' },
{ path: 'error', component: ErrorComponent },
{ path: 'vehicule', loadChildren: () => VehiculeModule },
/*{ path: 'vehicule', loadChildren: './modules/vehicule/[Link]
#VehiculeModule’ },*/
{ path: '', redirectTo: '/stagiaire', pathMatch: 'full' },
{ path: '**', redirectTo: '/error' }
];
@NgModule({
imports: [[Link](routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Boudriga Imed 213
sous-module Angular
Dé-commentons les routes dans [Link]
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
// + les autres imports de components
const routes: Routes = [
{ path: 'camion', component: CamionComponent },
{ path: 'voiture', component: VoitureComponent },
{ path: '', component: VoitureComponent }
];
@NgModule({
imports: [[Link](routes)],
exports: [RouterModule]
})
export class VehiculeRoutingModule { }
Boudriga Imed 214
sous-module Angular
Dans [Link], supprimer l'importation du module VehiculeModule
@NgModule({
declarations: [
AppComponent,
AdresseComponent,
StagiaireComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {
constructor() { [Link]('app-module'); }
}
Boudriga Imed 215
sous-module Angular
Ainsi, en saisissant
• /vehicule : le composant voiture sera affiché
• /vehicule/voiture : le composant voiture sera affiché
• /vehicule/camion : le composant camion sera affiché
Remarque
• Vérifier dans la console qu'en lançant l'application sur localhost:4200, seul le message
app-module est affiché
• Le message vehicule-module est affiché seulement si on demande une route d'un
composant du module vehicule ) lazy loading (chargement paresseux : charger les
modules à la demande)
Boudriga Imed 216
sous-module Angular
On peut aussi utiliser les promesses (solution utilisée depuis Angular 8) : seul mode de
chargement supporté par Ivy
const routes: Routes = [
{ path: 'stagiaire', component: StagiaireComponent },
{ path: 'stagiaire/:nom/:prenom', component: StagiaireComponent },
{ path: 'stagiaire', component: StagiaireComponent },
{ path: 'adresse', component: AdresseComponent },
{ path: 'trainee', redirectTo: '/stagiaire' },
{ path: 'error', component: ErrorComponent },
{
path: 'vehicule',
loadChildren: () => import('./modules/vehicule/[Link]').
then(m => [Link])
},
{ path: '', redirectTo: '/stagiaire', pathMatch: 'full' },
{ path: '**', redirectTo: '/error' }
];
Boudriga Imed 217
sous-module Angular
Exercice
• Créez deux nouveaux sous-modules cours et exercice (avec un module de routage pour
chacun)
• Déplacez les composants stagiaire, adresse et observable dans le sous-module cours et
tableau et calcul dans le sous-module exercice
• Les nouvelles routes vers les composants déplacés doivent commencer par /cours ou
/exercice
Boudriga Imed 218
sous-module Angular
Nouveau contenu de routes défini dans [Link]
const routes: Routes = [
{
path: 'vehicule',
loadChildren: () => import('./modules/vehicule/[Link]').then(m =>
[Link]
)
},
{
path: 'cours',
loadChildren: () => import('./modules/cours/[Link]').then(m => [Link])
},
{
path: 'exercice',
loadChildren: () => import('./modules/exercice/[Link]').then(m =>
[Link]
)
},
{ path: 'error', component: ErrorComponent },
{ path: '**', redirectTo: '/error' }
];
Boudriga Imed 219
Boudriga Imed 220
interaction entre composant
Premières formes d'interaction
• Une application Angular est composée de plusieurs composants
• En utilisant des formulaires et des liens, on peut envoyer des données d'un
composant à un autre
Autres formes d'interaction : parent-enfant
• Ajouter le sélecteur d'un premier composant dans le template d'un deuxième
composant
✓ on appelle le premier composant : composant fils
✓ on appelle le deuxième composant : composant parent
• Définir le sens de transmission de données par
✓ un composant web Angular : ng-content
✓ des décorateurs @Input(), @Output(), @ViewChild,@ViewChildren,
@ContentChild, @ContentChildren
Autres formes d'interaction : enfant-enfant ou parent-enfant
Le sélecteur du premier composant et celui du deuxième se trouvent au même niveau )
même parent : solution (Subject et Service)
Boudriga Imed 221
interaction entre composant
Avant de commencer
• Créons deux composants pere et fils
• Définissions une route /pere pour le composant pere
• Ajoutons le sélecteur du composant fils app-fils dans [Link]
Le fichier [Link]
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-fils',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class FilsComponent implements OnInit {
constructor() { }
ngOnInit() { }
}
Le fichier [Link]
<li>
Je suis un fils
</li>
Boudriga Imed 222
interaction entre composant
Le fichier [Link]
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-pere',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class PereComponent implements OnInit {
constructor() { }
ngOnInit() { }
}
Dans [Link], on définit trois fils
<ul>
<app-fils></app-fils>
<app-fils></app-fils>
<app-fils></app-fils>
</ul>
Boudriga Imed 223
interaction entre composant
Et si on veut passer du contenu texte à notre composant enfant, on fait :
<ul>
<app-fils>imed boudriga</app-fils>
<app-fils>saleh ben saleh</app-fils>
<app-fils>ali boudriga</app-fils>
</ul>
En testant le résultat est :
Je suis un fils
Je suis un fils
Je suis un fils
Le contenu ajouté par le pere n'apparaît pas.
Boudriga Imed 224
interaction entre composant
Pour que le composant fils puisse récupérer le contenu défini par le parent, on utilise
la balise ng-content
<li>
Je suis un fils : <ng-content></ng-content>
</li>
En testant le résultat est :
Je suis un fils : imed boudriga
Je suis un fils : saleh ben saleh
Je suis un fils : ali boudriga
Boudriga Imed 225
interaction entre composant
Pour que le composant fils puisse récupérer le contenu défini par le parent, on utilise
la balise ng-content
<li>
Je suis un fils : <ng-content></ng-content>
</li>
En testant le résultat est :
Je suis un fils : imed boudriga
Je suis un fils : saleh ben saleh
Je suis un fils : ali boudriga
Remarque
La balise ng-content a un attribut select qui prend comme valeur :
• le nom d'un attribut de n'importe quelle balise définie dans le composant parent
• le nom d'une classe CSS
• le nom d'une balise
Boudriga Imed 226
interaction entre composant
Dans [Link], on définit le nom et le prénom dans deux balises ayant
deux attributs différents : prenomContent et nomContent
<ul>
<app-fils>
<span prenomContent> imed </span>
<span nomContent> boudriga </span>
</app-fils>
<app-fils>
<span prenomContent> saleh </span>
<span nomContent> ben saleh </span>
</app-fils>
<app-fils>
<span prenomContent> ali </span>
<span nomContent> boudriga </span>
</app-fils>
</ul>
Dans [Link], on sélectionne les données selon les deux attributs
prenomContent et nomContent
<li>
Je suis un fils : mon nom est <ng-content select="[nomContent]"></ngcontent>
et mon prénom est <ng-content select="[prenomContent]"></ng-content>
</li>
Boudriga Imed 227
interaction entre composant
En testant le résultat est :
Je suis un fils : mon nom est boudriga et mon prénom est imed
Je suis un fils : mon nom est saleh et mon prénom est ben saleh
Je suis un fils : mon nom est boudriga et mon prénom est ali
Boudriga Imed 228
interaction entre composant
Dans [Link], on peut aussi définir des classes dont la valeur est soit
prenomContent ou nomContent
<ul>
<app-fils>
<span class=prenomContent> imed </span>
<span class=nomContent> boudriga </span>
</app-fils>
<app-fils>
<span class=prenomContent> saleh </span>
<span class=nomContent> ben saleh </span>
</app-fils>
<app-fils>
<span class=prenomContent> ali </span>
<span class=nomContent> boudriga </span>
</app-fils>
</ul>
Dans [Link], on sélectionne les données selon leurs classes
<li>
Je suis un fils : mon nom est <ng-content select=".nomContent"></ngcontent>
et mon prénom est <ng-content select=".prenomContent"></ng-content>
</li>
: Research Boudriga Imed 229
interaction entre composant
En testant le résultat est le même
Je suis un fils : mon nom est boudriga et mon prénom est imed
Je suis un fils : mon nom est ben saleh et mon prénom est saleh
Je suis un fils : mon nom est boudriga et mon prénom est ali
Boudriga Imed 230
interaction entre composant
Dans [Link], on peut aussi utiliser plusieurs balises différentes
<ul>
<app-fils>
<span> imed boudriga</span>
<a href="#"> link </a>
</app-fils>
<app-fils>
<span> saleh ben saleh</span>
<a href="#"> link </a>
</app-fils>
<app-fils>
<span> ali boudriga</span>
<a href="#"> link </a>
</app-fils>
</ul>
Dans [Link], on sélectionne les données selon les balises
<li>
Nom et prénom : <ng-content select="span"></ng-content>,
pour apprendre plus => <ng-content select="a"></ng-content>
</li>
Boudriga Imed 231
interaction entre composant
En testant le résultat est :
Nom et prénom : imed boudriga, pour apprendre plus => link
Nom et prénom : saleh ben saleh, pour apprendre plus => link
Nom et prénom : ali boudriga, pour apprendre plus => link
Boudriga Imed 232
interaction entre composant
En testant le résultat est :
Nom et prénom : imed boudriga, pour apprendre plus => link
Nom et prénom : saleh ben saleh, pour apprendre plus => link
Nom et prénom : ali boudriga, pour apprendre plus => link
Décorateurs disponibles pour l'interaction entre composants
@Input() : permet à un composant fils de récupérer des données de son composant
parent
@ViewChild() : permet à un composant parent de récupérer les données de son
composant enfant
@ViewChildren() : permet à un composant parent de récupérer les données de ses
composants enfants
@Output() : permet à un composant parent de récupérer des données de son
composant enfant
Boudriga Imed 233
interaction entre composant
Dans cet exemple
On définit dans [Link] deux attributs ordre et villeNaissance qui seront
affichés dans le template
Nouveau contenu de [Link]
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: 'app-fils',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class FilsComponent implements OnInit {
@Input() ordre: string;
@Input() villeNaissance: string;
constructor() { }
ngOnInit() { }
}
Modifions le fichier [Link]
<li>
Je suis le {{ ordre }} fils et suis de {{ villeNaissance }}
</li>
Boudriga Imed 234
interaction entre composant
Le fichier [Link]
import { Component, OnInit } from '@angular/core';
import { FilsComponent } from '../fils/[Link]';
@Component({
selector: 'app-pere',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class PereComponent implements OnInit {
tab: Array<string> = ['premier', 'deuxième', 'troisième'];
nord = 'Bizerte';
sud = 'djerba';
capitale = 'Tunis';
constructor() { }
ngOnInit() { }
}
Le fichier [Link]
<ul>
<app-fils [ordre]="tab[0]" [villeNaissance]="sud"></app-fils>
<app-fils [ordre]="tab[1]" [villeNaissance]="nord"></app-fils>
<app-fils [ordre]="tab[2]" [villeNaissance]="capitale"></app-fils>
</ul> Boudriga Imed 235
interaction entre composant
Le résultat est
Je suis le premier fils et suis de Djerba
Je suis le deuxième fils et suis de Bizerte
Je suis le troisième fils et suis de Tunis
Quelques interfaces prédéfinis dans Angular
• OnInit avec une méthode ngOnInit() qu'on utilise pour initialiser le composant
• OnChange avec une méthode ngOnChanges() qu'on utilise pour détecter les
changement de valeurs d'un fils
Boudriga Imed 236
interaction entre composant
Nouveau contenu de [Link]
import { Component, OnInit, Input, OnChanges } from '@angular/core';
@Component({
selector: 'app-fils',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class FilsComponent implements OnInit, OnChanges {
@Input() ordre: string;
@Input() villeNaissance: string;
constructor() { }
ngOnInit() { }
ngOnChanges(changes: SimpleChanges): void { [Link](changes); }
}
Remarque
• SimpleChanges est un objet JavaScript dont les cl ´es sont les attributs changés et les
valeurs sont des objets SimpleChange
• SimpleChange est un objet JavaScript contenant trois clés : previousValue,
currentValue et firstChange();
Boudriga Imed 237
interaction entre composant
Pour afficher tous les changements
import { Component, OnInit, Input, OnChanges } from '@angular/core';
@Component({
selector: 'app-fils',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class FilsComponent implements OnInit, OnChanges {
@Input() ordre: string;
@Input() villeNaissance: string;
constructor() { }
ngOnInit() { }
ngOnChanges(changes: SimpleChanges): void {
for (const key of [Link](changes)) {
[Link](key);
const obj = changes[key];
for (const cle of [Link](obj)) {
[Link](cle, obj[cle]);
}
}
}
} Boudriga Imed 238
interaction entre composant
Exercice
• Dans [Link], ajoutez deux input : un premier pour nom et un
deuxième pour ville
• Dans [Link], définissez deux variables nom et un deuxième pour la ville
et deux tableaux noms et villes
• Créez autant de app-fils que d'éléments dans noms (ou villes)
• Chaque fils récupére et affiche les valeurs envoyées par le composant père
• L'utilisateur pourra ajouter un nombre indéterminé de composant fils dans pere.
Boudriga Imed 239
interaction entre composant
Objectif
Récupérer les données du premier composant fils à partir d'un composant parent
Comment?
• Déclarer un composant fils comme attribut d'un composant parent et le décorer avec
@ViewChild()
• Utiliser cet attribut pour récupérer les données souhaitées
Boudriga Imed 240
interaction entre composant
Commençons par déclarer le composant fils comme attribut dans [Link]
et le décorer avec @ViewChild()
import { Component, OnInit, ViewChild } from '@angular/core';
import { FilsComponent } from '../fils/[Link]';
@Component({
selector: 'app-pere',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class PereComponent implements OnInit {
@ViewChild(FilsComponent) fils: FilsComponent;
tab: Array<string> = ['premier', 'deuxième', 'troisième'];
nord = 'Bizerte';
sud = 'Djerba';
capitale = 'Tunis';
constructor() { }
ngOnInit() { }
}
Boudriga Imed 241
interaction entre composant
Le décorateur @ViewChild() a un attribut static qui a par défaut la valeur false
import { Component, OnInit, ViewChild } from '@angular/core';
import { FilsComponent } from '../fils/[Link]';
@Component({
selector: 'app-pere',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class PereComponent implements OnInit {
@ViewChild(FilsComponent, { static: false }) fils: FilsComponent;
tab: Array<string> = ['premier', 'deuxième', 'troisième'];
nord = 'Bizerte';
sud = 'Djerba';
capitale = 'Tunis';
constructor() { }
ngOnInit() { }
}
{ static: false } car les attributs du composant enfant sont alimentés par le père et ne
sont donc pas initialisés dans le composant.
Boudriga Imed 242
interaction entre composant
on ne peut accéder aux attributs d'un composant enfant non-statique qu'après
initiation de la vue
import { Component, OnInit, ViewChild } from '@angular/core';
import { FilsComponent } from '../fils/[Link]';
@Component({
selector: 'app-pere',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class PereComponent implements OnInit {
@ViewChild(FilsComponent, { static: false }) fils: FilsComponent;
tab: Array<string> = ['premier', 'deuxi`eme', 'troisi`eme'];
nord = 'Bizerte';
sud = 'Djerba';
capitale = 'Tunis';
constructor() { }
ngOnInit() {
[Link]([Link]);
}
}
ce code génère une erreur car le composant fils est encore indéfini.
Boudriga Imed 243
interaction entre composant
En mettant { static: true }, on indique qu'il ne faut plus attendre l'initiation de la vue
(fils) car ses données sont statiques
import { Component, OnInit, ViewChild } from '@angular/core';
import { FilsComponent } from '../fils/[Link]';
@Component({
selector: 'app-pere',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class PereComponent implements OnInit {
@ViewChild(FilsComponent, { static: true }) fils: FilsComponent;
tab: Array<string> = ['premier', 'deuxi`eme', 'troisi`eme'];
nord = 'Bizerte';
sud = 'Djerba';
capitale = 'Tunis';
constructor() { }
ngOnInit() {
[Link]([Link]); // affiche undefined
}
}
Affecter une valeur à ordre dans [Link] et vérifier que cette valeur est
affichée `a la place de undefined.
Boudriga Imed 244
interaction entre composant
Remettons { static: false } et indiquons qu'il faut attendre que la vue soit initiée
pour accéder aux attributs
import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/
core';
import { FilsComponent } from '../fils/[Link]';
@Component({
selector: 'app-pere',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class PereComponent implements OnInit, AfterViewInit {
@ViewChild(FilsComponent, { static: false }) fils: FilsComponent;
tab: Array<string> = ['premier', 'deuxi`eme', 'troisi`eme'];
nord = 'Bizerte';
sud = 'Djerba';
capitale = 'Tunis';
constructor() { }
ngOnInit() { }
ngAfterViewInit(): void {
[Link]([Link]); // affiche premier
}
} Boudriga Imed 245
interaction entre composant
Objectif
Récupérer les données de tous les composants fils `a partir d'un composant parent
Comment?
• Déclarer un QueryList (tableau) de composant fils comme attribut d'un composant
parent et le décorer avec @ViewChildren()
• Utiliser cet attribut pour récupérer les données souhaitées
H
Boudriga Imed 246
interaction entre composant
Utilisons @ViewChildren pour récupérer un tableau de composant fils
import { Component, OnInit, AfterViewInit, ViewChildren, QueryList }
from '@angular/core';
import { FilsComponent } from '../fils/[Link]';
@Component({
selector: 'app-pere',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class PereComponent implements OnInit, AfterViewInit {
@ViewChildren(FilsComponent) fils: QueryList<FilsComponent>;
tab: Array<string> = ['premier', 'deuxi`eme', 'troisi`eme'];
nord = 'Bizerte';
sud = 'Djerba';
capitale = 'Tunis';
constructor() { }
ngOnInit() { }
ngAfterViewInit(): void {
[Link](elt => [Link](elt));
// affiche les trois FilsComposant dans la console
}
} Boudriga Imed 247
interaction entre composant
Exercice list-item
• Créons deux composants list et item.
• Chaque composant item reçoit le texte et sa couleur d'affichage du composant list.
• Dans le composant list, on a deux zones de saisie : la première pour le texte, la
deuxième pour la couleur du texte à afficher ( à saisir en anglais).
• En cliquant sur le bouton ajouter du composant list, un nouveau composant item
s'ajoute (s'affiche) au composant (dans la page) avec la couleur indiquée par
l'utilisateur.
• L'utilisateur pourra ajouter un nombre indéterminé de texte.
• Depuis le composant list, on veut permettre à l'utilisateur de modifier la couleur
d'un ou tous les composant(s) item.
• Pour cela, on ajoute deux nouvelles zones de saisie : une pour l'indice du composant
item (qu'on souhaite modifier sa couleur) et la deuxième est la couleur qu'on veut
lui attribuer.
• Si l'indice n'existe pas (négatif ou supérieur ou égal au nombre des composants
item), on modifie la couleur de tous les item.
• Sinon, on modifie seulement la couleur du composant item ayant cet indice.
H
Boudriga Imed 248
interaction entre composant
Objectif
Créer un composant titre pour l'utiliser chaque fois qu'on a un composant père avec ses
composants fils
Comment?
• Créer un composant [Link]
• Utiliser la balise @ContentChild pour récupérer le contenu d'un noeud enfant défini
par la balise ng-content
Boudriga Imed 249
interaction entre composant
Le fichier [Link]
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: 'app-titre',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class TitreComponent implements OnInit {
@Input() valeur: string;
@Input() couleur: string;
constructor() { }
ngOnInit() {
}
}
Le fichier [Link]
<h1 [ngStyle]="{color: couleur}">{{ valeur }}</h1>
Boudriga Imed 250
interaction entre composant
Dans [Link], on ajoute la balise app-pere
contenant une balise app-titre
<app-pere>
<app-titre [couleur]="'red'" [valeur]="'Mes contacts'"></app-titre>
</app-pere>
<router-outlet></router-outlet>
N'oublions pas les apostrophes autour de red et Mes contacts
Dans [Link], on récupére sélectionne le titre dans la balise ng-content
<ng-content select="app-titre"></ng-content>
<ul>
<app-fils [ordre]="tab[0]" [villeNaissance]="sud"></app-fils>
<app-fils [ordre]="tab[1]" [villeNaissance]="nord"></app-fils>
<app-fils [ordre]="tab[2]" [villeNaissance]="capitale"></app-fils>
</ul>
Boudriga Imed 251
interaction entre composant
Dans [Link], on peut récupérer les attributs du composant titre
import { Component, OnInit, AfterContentInit, ViewChildren,
ContentChild } from '@angular/core';
import { TitreComponent } from '../titre/[Link]';
@Component({
selector: 'app-pere',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class PereComponent implements OnInit, AfterContentInit {
@ContentChild(TitreComponent, { static: false }) titre:
TitreComponent;
tab: Array<string> = ['premier', 'deuxième', 'troisième'];
nord = 'Bizerte';
sud = 'Djerba';
capitale = 'Tunis';
constructor() { }
ngOnInit() { }
ngAfterContentInit(): void {
[Link]([Link]);
}
} Boudriga Imed 252
interaction entre composant
Explication
• @ContentChild permet de récupérer le premier composant sélectionné par ng-
content
• @ContentChildren permet de récupérer tous les composants sélectionnés par ng-
content
Comment?
• Comme pour @ViewChildren(), déclarer un QueryList (tableau) de composant et le
décorer avec @ContentChildren()
• Utiliser cet attribut pour récupérer les données souhaitées
Avant de commencer
• Créons deux composants parent et child
• Définissions une route /parent pour le composant parent
Boudriga Imed 253
interaction entre composant
Dans cet exemple
• Chaque élément child aura un champ texte pour saisir une note et un bouton pour
envoyer la valeur au composant parent
• Le bouton sera désactivé après envoi
• Le sélecteur du composant fils app-child sera ajouté dans [Link]
• Chaque fois que le composant parent reçoit une note d'un de ses fils, il recalcule la
moyenne et il l'affiche
Le fichier [Link]
<h6> {{ nom }} </h6>
<input type=number name=note [(ngModel)]=note>
<button (click)="send()" [disabled]="buttonStatus">
Send
</button>
Boudriga Imed 254
interaction entre composant
Le fichier [Link]
import { Component, OnInit, Input, Output, EventEmitter } from '
@angular/core';
@Component({
selector: 'app-child',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class ChildComponent implements OnInit {
@Input() nom: string;
@Output() message = new EventEmitter<number>();
note: number;
buttonStatus: boolean = false;
constructor() { }
ngOnInit() { }
send() {
[Link]([Link]);
[Link] = true;
}
}
Boudriga Imed 255
interaction entre composant
Le fichier [Link]
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-parent',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class ParentComponent implements OnInit {
moyenne = 0;
somme = 0;
nbr = 0;
enfants = ['imed', 'salah', 'ali'];
constructor() { }
ngOnInit() { }
computeAvg(note: number) {
[Link] += note;
[Link]++;
[Link] = [Link] / [Link];
}
}
Boudriga Imed 256
interaction entre composant
Le fichier [Link]
<h3> Moyenne de mes enfants {{ moyenne }} </h3>
<app-child *ngFor="let enfant of enfants" [nom]="
enfant" (message)="computeAvg($event)">
</app-child>
Boudriga Imed 257
Boudriga Imed 258
RxJS
Programmation réactive
• paradigme de programmation oriente flux de données et propagation des
changements
• inspiré du patron de conception observable
• Deux concepts importants :
✓ Observable : une fonction retournant un flux de valeurs à un observateur
de manière synchrone ou asynchrone. Un observable s'exécute s'il y a un
observateur (observer) et un abonnement (avec la méthode subscribe())
✓ Observer : un élément (objet) qui se souscrit (subscribe()) à un observable
pour recevoir les changements et exécuter une suite de code
RxJS : Reactive extensions for JavaScript
Boudriga Imed 259
RxJS
La méthode subscribe() prend trois paramètre
• la première se déclenche à chaque fois que l'observable émet de nouvelles données
(ces données sont reçues comme paramètre)
• 2
• la deuxième se déclenche si l'observable émet une erreur, et reçoit cette erreur
comme paramètre
• la troisième se déclenche lorsque l'observable se termine, et ne reçoit aucun
paramètre.
Pour commencer
• créez un composant observable
• ajoutez <app-observable>< /app-observable> dans [Link]
Boudriga Imed 260
RxJS
Le fichier [Link]
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-observable',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class ObservableComponent implements OnInit {
status = '';
tab: Array<number> = [];
constructor() { }
ngOnInit() { }
}
Le fichier [Link]
<h1>éléments</h1>
<ul>
<li *ngFor="let elt of tab">
{{ elt }}
</li>
</ul>
<div>{{ status }}</div> Boudriga Imed 261
RxJS
Pour créer un observable, on peut utiliser la méthode of() qui convertit un ensemble
de paramètres en observable
import { Component, OnInit } from '@angular/core';
import { Observable, of } from 'rxjs';
@Component({
selector: 'app-observable',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class ObservableComponent implements OnInit {
status = '';
tab: Array<number> = [];
constructor() { }
ngOnInit(): void {
const observable: Observable<number> = of(1, 2, 3);
}
}
Boudriga Imed 262
RxJS
On peut utiliser from() pour construire un observable à partir d'un tableau
import { Component, OnInit } from '@angular/core';
import { Observable, from } from 'rxjs';
@Component({
selector: 'app-observable',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class ObservableComponent implements OnInit {
status = '';
tab: Array<number> = [];
constructor() { }
ngOnInit() {
const tableau = [1, 2, 3];
const observable: Observable<number> = from(tableau);
}
}
Boudriga Imed 263
RxJS
On peut utiliser range() pour définir un interval de nombre entier
import { Component, OnInit } from '@angular/core';
import { Observable, range } from 'rxjs';
@Component({
selector: 'app-observable',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class ObservableComponent implements OnInit {
status = '';
tab: Array<number> = [];
constructor() { }
ngOnInit() {
const observable: Observable<number> = range(1, 3);
}
}
Boudriga Imed 264
RxJS
Lorsqu'un observateur s'abonne à notre observable, il peut implémenter trois
méthodes pour spécifier ce qu'il faut faire
ngOnInit() {
const tableau = [1, 2, 3];
const observable: Observable<number> = from(tableau);
const observer: Observer<number> = {
next: (value) => {
[Link](value);
},
error: (error) => {
[Link] = error;
},
complete: () => {
[Link] = 'fini';
}
};
}
Boudriga Imed 265
RxJS
On peut aussi directement faire
ngOnInit() {
const tableau = [1, 2, 3];
const observable: Observable<number> = from(tableau);
[Link](
(value) => {
[Link](value);
},
(error) => {
[Link] = error;
},
() => {
[Link] = 'fini';
}
);
}
L'émission de valeurs s'effectue d'une manière synchrone et par conséquent on ne
voit pas la réception des éléements dans le navigateur.
Boudriga Imed 266
RxJS
Pour avoir une exécution asynchrone, on utilise la fonction interval(1000)
une infinité de valeurs incrémentielles commençant de 0 : une valeur par seconde
ngOnInit() {
const observable: Observable<number> = interval(1000);
[Link](
(value) => {
[Link](value);
},
(error) => {
[Link] = error;
},
() => {
[Link] = 'fini';
}
);
}
Boudriga Imed 267
RxJS
Pour avoir une exécution asynchrone, on utilise la fonction timer(3000,1000) qui
envoie une infinité de valeurs incrémentielles commençant de 0 : une valeur par
seconde la première valeur sera envoyée après 3 secondes
ngOnInit() {
const observable: Observable<number> = timer(3000, 1000);
[Link](
(value) => {
[Link](value);
},
(error) => {
[Link] = error;
},
() => {
[Link] = 'fini';
}
);
}
Les deux fonctions timer() et interval() ne se terminent jamais.
Boudriga Imed 268
RxJS
Pour éviter les problèmes de mémoire, il faut penser à se désabonner à la destruction du composant
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Observable, Subscription, timer } from 'rxjs';
@Component({
selector: 'app-observable',
templateUrl: './[Link]',
styleUrls: ['./[Link]']
})
export class ObservableComponent implements OnInit, OnDestroy {
tab: Array<number> = [];
status = '';
constructor() { }
subscription: Subscription;
ngOnInit() {
const observable: Observable<number> = timer(3000, 1000);
[Link] = [Link](
(value) => { [Link](value); },
(error) => { [Link] = error; },
() => { [Link] = 'fini'; }
);
}
ngOnDestroy() { [Link](); }
}
Boudriga Imed 269
RxJS
Pour indiquer le nombre d'élément à émettre, on utilise la méthode pipe() qui nous
permet de faire appel à l'opérateur take()
ngOnInit() {
const observable: Observable<number> = interval(1000).pipe(take(10));
[Link](
(value) => {
[Link](value);
},
(error) => {
[Link] = error;
},
() => {
[Link] = 'fini';
}
);
}
On peut importer les opérateurs ainsi : import f take g from 'rxjs/operators';
Boudriga Imed 270
RxJS
Pour indiquer le nombre d'élément à émettre, on utilise la méthode pipe() qui nous
permet de faire appel à l'opérateur take()
ngOnInit() {
const observable: Observable<number> = interval(1000).pipe(take(10));
[Link](
(value) => {
[Link](value);
},
(error) => {
[Link] = error;
},
() => {
[Link] = 'fini';
}
);
}
On peut importer les opérateurs ainsi : import { take } from 'rxjs/operators';
Boudriga Imed 271
RxJS
On peut combiner les operateurs en appliquant une modification sur les 10 éléments
reçus
ngOnInit() {
const observable: Observable<number> = interval(1000).pipe(
take(10),
map(elt => elt + 3)
);
[Link](
(value) => {
[Link](value);
},
(error) => {
[Link] = error;
},
() => {
[Link] = 'fini';
}
);
}
Boudriga Imed 272
RxJS
On peut aussi filtrer les éléments pairs
ngOnInit() {
const observable: Observable<number> = interval(1000).pipe(
take(10),
map(elt => elt + 3),
filter(elt => elt % 2 === 0)
);
[Link](
(value) => {
[Link](value);
},
(error) => {
[Link] = error;
},
() => {
[Link] = 'fini';
}
);
}
Boudriga Imed 273
RxJS
On peut aussi compter les éléments selon un critère
ngOnInit() {
const observable: Observable<number> = interval(1000).pipe(
take(10),
count(elt => elt % 2 === 0)
);
[Link](
(value) => {
[Link](value);
},
(error) => {
[Link] = error;
},
() => {
[Link] = 'fini';
}
);
}
Boudriga Imed 274
RxJS
On peut sélectionner le maximum
ngOnInit() {
const observable: Observable<number> = interval(1000).pipe(
take(10),
max()
);
[Link](
(value) => {
[Link](value);
},
(error) => {
[Link] = error;
},
() => {
[Link] = 'fini';
}
);
}
Boudriga Imed 275
RxJS
On peut fusionner plusieurs observables grâce à la fonction merge()
ngOnInit() {
const tableau = [1, 2, 3];
const observable1: Observable<number> = of(1, 2, 3);
const observable2: Observable<number> = of(4, 5, 6);
const merged = merge(
observable1,
observable2
);
[Link](
(value) => {
[Link](value);
},
(error) => {
[Link] = error;
},
() => {
[Link] = 'fini';
}
);
} Boudriga Imed 276
RxJS
Subject
• Il a à la fois le rôle d'un observateur et d'un observable
• Il autorise la souscription de plusieurs observateurs
Dans ngOnInit(), commençons par déclarer un Subject
const subject = new Subject<number>();
Plusieurs observateurs peuvent s'abonner à notre subject
[Link]({
next: (value) => [Link]('A : ${value}')
});
[Link]({
next: (value) => [Link]('B : ${value}')
});
Introduisons deux nouvelles valeurs à notre subject
[Link](1);
[Link](2);
// le résultat est
// A : 1
// B : 1
// A : 2
Boudriga Imed 277
// B : 2
RxJS
Un Subject est aussi un observateur, il peut donc s'abonner à un observable
ngOnInit() {
const subject = new Subject<number>();
[Link]({
next: (value) => [Link]('A : ${value}')
});
[Link]({
next: (value) => [Link]('B : ${value}')
});
[Link](subject);
}
Le résultat est
// A : 1
// B : 1
// A : 2
// B : 2
// A : 3
// B : 3
Boudriga Imed 278
RxJS
Remarque
• Un observable ne peut avoir qu'un seul observateur
• Un observable peut utiliser l'opérateur multicast et un subject pour avoir plusieurs
observateurs
• L'observable devient ainsi un ConnectableObservable et utilise la méthode connect
pour propager les changements
Boudriga Imed 279
RxJS
Exemple
const observable: Observable<number> = from([1, 2, 3]);
const subject = new Subject<number>();
const multicasted = [Link](multicast(subject)) as
ConnectableObservable<number>;
[Link]({
next: (value) => [Link]('A : ${value}')
});
[Link]({
next: (value) => [Link]('B : ${value}')
});
[Link]();
Le résultat est
// A : 1
// B : 1
// A : 2
// B : 2
// A : 3
// B : 3
Boudriga Imed 280
RxJS
Pour se désabonner, on appelle la méthode unsubscribe() depuis la souscription
ngOnInit() {
const observable: Observable<number> = interval(1000).pipe(take(10)
);
const subject = new Subject<number>();
const multicasted = [Link](multicast(subject)) as
ConnectableObservable<number>;
const a = [Link]({
next: (value) => [Link]('A : ${value}')
});
const b = [Link]({
next: (value) => [Link]('B : ${value}')
});
[Link]();
setTimeout(() => [Link](), 3000);
setTimeout(() => [Link](), 5000);
}
Boudriga Imed 281
RxJS
Behavior Subject
• Une des variantes de Subject fonctionnant avec la notion de valeur actuelle
• Il stocke la dernière valeur émise par ses observateurs
• Chaque fois qu'un nouvel observateur s'abonne, il reçoit immédiatement la valeur
actuelle
• Le constructeur de la classe BehaviorSubject prend comme paramètre la valeur
initiale
Boudriga Imed 282
RxJS
Exemple
ngOnInit() {
const subject = new BehaviorSubject(0);
[Link]({
next: (value) => [Link]('A : ${value}')
});
[Link](1);
[Link](2);
[Link]({
next: (value) => [Link]('B : ${value}')
});
[Link](3);
}
Le résultat est
// A : 0
// A : 1
// A : 2
// B : 2
// A : 3
// B : 3
Boudriga Imed 283
RxJS
ReplaySubject
• Une des variantes de Subject fonctionnant avec la notion de nombre de valeurs à
conserver dans l'historique
• Il conserve un nombre de valeurs dans l'historique afin qu'elles puissent être
envoyées aux nouveaux abonnés.
• Le constructeur de la classe ReplaySubject prend comme paramètre le nombre de
valeurs à conserver dans l'historique
Boudriga Imed 284
RxJS
Exemple
ngOnInit() {
const subject = new ReplaySubject<number>(2);
[Link](0);
[Link]({
next: (value) => [Link]('A : ${value}')
});
[Link](1);
[Link](2);
[Link]({
next: (value) => [Link]('B : ${value}')
});
[Link](3);
}
Le résultat est
// A : 0
// A : 1
// A : 2
// B : 1
// B : 2
// A : 3
// B : 3 Boudriga Imed 285
RxJS
Async Subject
• Une des variantes de Subject
• Il envoie seulement la dernière valeur à ses observateurs et c'est lorsqu'il finit quel
que soit l'ordre de leur abonnement
• Pour signaler sa fin, il appelle la méthode complete
Boudriga Imed 286
RxJS
Exemple
ngOnInit() {
const subject = new AsyncSubject();
[Link](0);
[Link]({
next: (value) => [Link]('A : ${value}')
});
[Link](1);
[Link](2);
[Link]({
next: (value) => [Link]('B : ${value}')
});
[Link](3);
[Link]();
}
Le résultat est
// A : 3
// B : 3
Boudriga Imed 287