0% ont trouvé ce document utile (0 vote)
22 vues287 pages

Cours Angular

Transféré par

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

Cours Angular

Transféré par

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

Angular

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&param2=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

Vous aimerez peut-être aussi