Angular
Angular est un framework open-source développé par Google, utilisé pour créer des applications web dynamiques et maintenables en utilisant TypeScript.
Informations
Date de publication :
Date de modification :
Catégories : javascript, html, css, scss, api
Auteur :
meezyr
Sommaire
- 1. Nouveau projet
- 2. Les bases
- 3. Composants
- 4. Interactions
- 5. Directives
- 6. Services injectable
- 7. Routing
- 8. Formulaires
- 9. Pipes
- 10. Http Requests
- 11. Local Storage
- 12. Modules
- 13. Signaux
- 14. Server-side rendering
- 15. Animations
- 16. Service worker
- 17. Tests
- 18. Création d’une interface
- 19. Dépendances et Outils

Angular est un Framework JavaScript qui permet de créer des Single-Page-Applications (SPAs), c’est-à-dire que la page ne change jamais et qu’il n’y qu’un seul fichier HTML et du JavaScript qui permettent d’afficher un site entier sur un navigateur. Pour en savoir plus sur Angular, consultez la documentation officielle.
1. Nouveau projet
1.1 Installation de NodeJS et npm
Il faut tout d’abord installer NodeJS avant d’installer le CLI d’Angular, télécharger la version recommandée de NodeJS sur le documentation officielle et installer là.
Si c’est déjà le cas, vérifier bien que NodeJS et npm soient à jour. Pour NodeJS, téléchargez la dernière version et désinstallez d'abord toutes les versions installées sur votre ordinateur. Pour mettre à jour npm utilisez cette commande :
npm install -g npm
1.2 Installation du CLI de Angular
Pour installer la dernière version du CLI de Angular, utilisez cette commande :
npm install -g @angular/cli
Pour mettre à jour le CLI de Angular, utilisez les commandes suivantes :
npm uninstall -g angular-cli @angular/cli
npm cache verify
npm install -g @angular/cli
1.3 Création d’un projet
Pour créer un nouveau projet avec Angular, il faut utiliser la commande suivante dans le dossier souhaité :
npm install
ng new nom-du-projet --strict false
La propriété --strict false permet de simplifier l’écriture du code par endroits. La propriété --standalone false permet de notamment de générer automatiquement le fichier app.module.ts.
1.4 Lancement d’un projet
Afin de démarrer une application avec Angular, il faut utiliser la commande suivante :
ng serve
2. Les bases
2.1 Création d’un composant
Afin de créer rapidement un nouveau composant, on utilise cette commande :
ng generate component nom-du-composant
Plusieurs propriétés sont disponibles, notamment afin de générer le code d’un composant dans seulement un fichier, grâce aux propriétés :
--inline-template: Permet de ne pas générer de fichierHTMLet de mettre directement leHTMLdans le fichierTypeScript.--inline-style: Permet de ne pas générer de fichierCSSet de mettre directement leCSSdans le fichierTypeScript.--skip-tests: Permet de ne pas générer de fichier de test du composant.
Voici la structure du code d’un composant en seulement un fichier :
import {Component} from '@angular/core';
@Component({
selector: 'app-welcome',
template: `
<h1>Welcome to Angular!</h1>
`,
standalone: true,
styles : [`
h1 {
color : red ;
}
`],
});
export class WelcomeComponent {}
2.2 Affichage d’informations
Pour afficher une information d’un composant, on peut dans la class du fichier TypeScript inscrire des choses qui seront envoyé directement dans la template :
export class WelcomeComponent {
nom : string = 'Doe';
age : number = 18;
}
Puis dans la template, on peut récupérer les informations de cette manière :
<p>{{ 'Bienvenue' }} Monsieur {{ nom }}, vous avez {{ age }} ans.</p>
On peut utiliser également des méthodes dans la class d’un composant, afin par exemple d’envoyer des informations au template HTML :
export class WelcomeComponent {
nom : string = 'Doe';
prenom : string = 'John';
getFullName() {
return this.nom + ' ' + this.prenom ;
}
}
Puis pour appeler cette méthode dans la template, il faut utiliser le code suivant :
<p>Bienvenue Monsieur {{ getFullName() }}.</p>
2.3 Conditions ngIf et ngSwitch
Pour créer une condition dans la partie template de Angular, on utilise ngIf, comme dans l’exemple suivant :
<p *ngIf="isActive">C’est ok !</p> // Si isActive est à true le texte s’affiche
L’étoile (*) devant ngIf est nécessaire car ngIf modifie la structure du DOM, ngIf fonctionne avec true ou false uniquement.
On peut aussi utiliser la condition else avec ngIf, comme dans l’exemple suivant :
<p *ngIf="isActive; else noActive">C’est ok !</p> // Si isActive est à true le texte s’affiche
<ng-template #noActive><p>Pas ok !</p></ng-template>
Pour en savoir plus, consultez la documentation officielle sur les conditions.
Il existe une nouvelle façon de faire des conditions avec Angular 17 :
@if ( uneCondition ) {
<p>Visible uniquement si 'uneCondition' est vrai.</p>
}
Pour en savoir plus, consultez la documentation officielle.
Il existe une autre directive qui permet de faire des conditions, le ngSwitch qui fonctionne comme un switch case en JavaScript. Pour en savoir plus, consultez la documentation officielle.
2.4 Boucle ngFor
On peut afficher des tableaux de données sous forme de liste grâce à ngFor, comme dans l’exemple suivant :
export class WelcomeComponent implements OnInit {
salut = ['Hello', 'Bonjour', 'Hola'];
constructor() { }
ngOnInit() { }
onCreateSalut() {
this.salut.push(this.salutName);
}
}
Puis dans le template, utilisez ce code :
<input type="text" [(ngModel)]="salutName">
<button (click)="onCreateSalut()">Add salut</button>
<p *ngFor="let item of salut">{{ item }}</p>
Pour obtenir l’index avec ngFor, utilisez ce code :
<p *ngFor="let item of salut; let i = index">{{ i }} : {{ item }}</p>
Pour en savoir plus, consultez la documentation officielle.
Il existe une nouvelle façon de faire avec Angular 17 :
@for (item of items; track item.id) {
<li>{{ item.title }}</li>
}
Pour en savoir plus, consultez la documentation officielle.
2.5 Directives ngStyle et ngClass
On peut gérer le style dynamiquement dans une template grâce à ngStyle, comme dans l’exemple suivant :
<p [ngStyle]="{backgroundColor : getColor()}">Test</p> // getColor() retourne une couleur
Pour en savoir plus, consultez la documentation officielle.
On peut gérer les class dynamiquement dans un template grâce à ngClass, comme dans l’exemple suivant :
<p [ngClass]="{une-class : classStatus === 'oui'}">Test</p> // classStatus est une propriété et retourne oui ou non
Pour en savoir plus, consultez la documentation officielle.
2.6 Références locales
Dans les templates, on peut utiliser des références locales qui permettent de faire passer des données entre deux éléments, comme dans l’exemple suivant :
<input type="text" #inputTest>
<button (click)="onAddTest(inputTest)">Make a test</button>
2.7 Importation de modules
Pour l’importation de modules fréquemment utilisé, on peut utiliser ngModule afin d’importer une fois les modules pour l’ensemble des composants. Pour en savoir plus, consultez la documentation officielle.
2.8 Variables d'environnement
On peut créer des variables d'environnement dans Angular, pour en savoir plus, consultez la documentation officielle.
3. Composants
3.1 Afficher un composant
Pour afficher un composant à partir d’un autre composant, il y a plusieurs solutions, voici le code de base :
import {Component, OnInit} from '@angular/core';
@Component({
selector: 'app-welcome',
template: `
<h1>Welcome to Angular!</h1>
`,
standalone: true,
});
export class WelcomeComponent implements OnInit {
constructor() { }
ngOnInit() { }
}
Afin d’afficher ce composant, il faut mettre ce code dans un autre composant :
<app-welcome></app-welcome>
// Ou
<app-welcome/>
Mais d’autres techniques existent :
selector: '[app-welcome]',
// Affichage :
<div app-welcome></div>
selector: '.app-welcome',
// Affichage :
<div class="app-welcome"></div>
3.2 Propriétés personnalisées
On peut créer des propriétés personnalisées pour un composant, afin de passer des données d’un composant à un autre, voici un exemple :
export class WelcomeComponent implements OnInit {
@Input element: {type: string, name: string, content: string};
constructor() { }
ngOnInit() { }
}
Puis dans la template d’un autre composant, utilisez ce code :
<app-welcome *ngFor="let welcomeElement of welcomeElements" [element]="welcomeElement"></app-welcome>
On peut également assigner un alias à une propriété, comme dans l’exemple suivant :
@Input('welcome') element: {type: string, name: string, content: string};
// Puis
<app-welcome *ngFor="let welcomeElement of welcomeElements" [welcome]="welcomeElement"></app-welcome>
On peut également créer des setters et des getters, pour en savoir plus, consultez la documentation officielle.
3.3 Projection du contenu
On peut projeter le contenu d’un composant dans un autre, comme dans l’exemple suivant :
// Dans la template de PremierComponent
<deuxieme-component><p>test</p></deuxieme-component>
// Dans la template de DeuxiemeComponent
<div>
<ng-content></ng-content> // Affiche <p>test</p>
</div>
3.4 Afficher les requêtes
Pour afficher les requêtes, on utilise @ViewChild comme ceci :
@ViewChild ( 'testContentInput' , { static : true }) testContentInput : ElementRef;
Pour plus d’informations, consultez la documentation officielle.
3.5 Lifecycle
Le cycle de vie d’un composant est le nombre d’étapes qu’il y a entre sa création et sa destruction, pour en savoir plus, consultez la documentation officielle.
4. Interactions
4.1 Utilisation de boutons
Pour faire une action quand on clique sur un bouton dans Angular, on doit créer l’action dans le constructeur de la class du composant :
export class WelcomeComponent implements OnInit {
allowSayWelcome = false ;
constructor() {
setTimeout(() => {
this.allowSayWelcome = true ;
}, 2000);
}
ngOnInit() { }
}
Dans la template HTML, on peut utiliser ce code :
<button [disabled]="allowSayWelcome">Say Welcome</button>
On peut également faire le contraire de ce qui es indiqué dans allowSayWelcome, simplement en ajoutant un point d’exclamation :
<button [disabled]=" !allowSayWelcome">Say Welcome</button>
4.2 Property binding et Event binding
Pour afficher un texte, on peut également utiliser la liaison de propriété (property binding), comme dans l’exemple suivant :
<button [disabled]="!allowSayWelcome">Say Welcome</button>
<p [innerText]="allowSayWelcome"></p> // Affiche true ou false
La liaison d’évènements (event binding) permet de déclanchement d’une action grâce à un évènement, comme dans l’exemple suivant :
export class WelcomeComponent implements OnInit {
textWelcome = 'Bienvenue à vous !';
constructor() { }
ngOnInit() { }
onCreateWelcome() {
this.textWelcome = 'Au revoir, à bientôt !';
}
}
Puis dans la template HTML, on peut utiliser ce code :
<button (click)="onCreateWelcome()">Say Welcome</button>
<p>{{ textWelcome }}</p> // Affiche 'Bienvenue à vous !' ou 'Au revoir, à bientôt !'
On peut aussi utiliser les données d’un input pour les afficher en direct, comme ceci :
export class WelcomeComponent implements OnInit {
textCustom = '';
constructor() { }
ngOnInit() { }
onUpdateWelcome(event : Event) {
this.textCustom = (<HTMLInputElement>event.target).value;
}
}
Puis dans la template HTML, on peut utiliser ce code :
<input type="text" (input)="onUpdateWelcome($event)">
<p>{{ textCustom }}</p> // Affiche le texte personnalisé du champs de texte
On peut également effectuer une liaison de données bidirectionnelle, attention il faut d’abord importer FormsModule avant d’utiliser le code suivant :
export class WelcomeComponent implements OnInit {
textCustom = 'Bonjour !';
constructor() { }
ngOnInit() { }
onUpdateWelcome(event : Event) {
this.textCustom = (<HTMLInputElement>event.target).value;
}
}
Puis dans la template HTML, on peut utiliser ce code :
<input type="text" (input)="onUpdateWelcome($event)">
<input type="text" [(ngModel)]="textCustom"> // Affiche la valeur personnalisée du champs de texte
<p>{{ textCustom }}</p> // Affiche la valeur personnalisée du champs de texte
On peut créer un bouton de reset avec une condition :
<button [disabled]="textCustom === ''" (click)="textCustom = ''">Reset</button>
Pour en savoir plus, consultez la documentation officielle sur le binding.
4.3 Événements
Pour les événements, on ne peut pas utiliser onclick mais uniquement click (=> (click)). Il existe plusieurs événements différents, en voici la liste :
- (
click) : L'événementclickse produit lorsqu'un élément est cliqué. - (
change) : L'événementchangeest déclenché lorsqu'une modification se produit sur les éléments de liaison, tels queselect,Textarea,inputet d'autres éléments. - (
dblclick) : L'événement dedblclickse produit lorsqu'un élément est cliqué deux fois. - (
blur) : L'événementblurse déclenche lorsqu'un élément a perdu le focus. - (
submit) : L'événementsubmitse déclenche lorsque vous cliquez sur le bouton ou lorsque vous saisissez letype submit. - (
focus) : L'événementfocusse déclenche lorsqu'un élément a reçu le focus. - (
scroll) : L'événementscrollse déclenche lorsque la vue du document a défilé. - (
keyup) : Lorsqu'un utilisateur appuie et relâche une touche, un événementkeyupse produit et est principalement utilisé avec les champs de saisie. C'est l'un des événements du clavier. - (
keydown) : L'événementkeydownest déclenché lorsqu'une touche est enfoncée. C'est l'un des événements du clavier. - (
keypress) : L'événementkeypressest déclenché lorsqu'une touche qui produit une valeur de caractère est enfoncée. C'est l'un des événements du clavier. - (
mousedown) : L'événementmousedownest déclenché sur un élément lorsqu'un bouton du périphérique de pointage est enfoncé alors que le pointeur se trouve à l'intérieur de l'élément et est un événement de souris. - (
mouseup) : L'événementmouseupse produit lorsqu'un utilisateur relâche le bouton de la souris sur un élément et est un événement de souris. - (
mouseenter) : L'événementmouseenterse produit lorsque le pointeur de la souris est déplacé sur un élément et est un événement de souris. - (
mouseleave) : L'événementmouseleavese produit lorsque le pointeur de la souris est déplacé hors d'un élément et est un événement de souris. - (
mousemove) : L'événementmousemovese produit lorsque le pointeur se déplace alors qu'il se trouve sur un élément et est un événement de souris. - (
mouseover) : L'événementmousemovese produit lorsque le pointeur se déplace alors qu'il se trouve sur un élément et est un événement de souris. - (
mouseup) : L'événementmouseupse produit lorsqu'un utilisateur relâche le bouton de la souris sur un élément et est un événement de souris. - (
copy) : L'événementcopyse produit lorsque l'utilisateur copie le contenu d'un élément. - (
paste) : L'événementpastese produit lorsque l'utilisateur colle le contenu d'un élément. - (
cut) : L'événementcutse produit lorsque l'utilisateur coupe le contenu d'un élément. - (
drag) : L'événementdragse produit lorsqu'un élément ou une sélection de texte est déplacé. - (
drop) : L'événementdropse produit lorsque les données glissées sont supprimées. - (
dragover) : L'événementdragoverse produit lorsqu'un élément déplaçable ou une sélection de texte est glissé sur une cible de dépôt valide. - (
input) : L'événementinputse produit lorsqu'un élément reçoit une entrée de l'utilisateur.
On peut créer des évènements personnalisés, comme dans l’exemple suivant :
// Composant WelcomeComponent
export class WelcomeComponent {
welcomeElements = [{state: 0, language: 'anglais', content: 'Hello'}];
onWelcomeAdded(welcomeData: {welcomeLang: string, welcomeText: string}) {
this.welcomeElements.push({
state: 1,
language: welcomeData.welcomeLang,
content: welcomeData.welcomeText,
});
}
}
// Composant SayHelloComponent
export class SayHelloComponent implements OnInit {
@Output() welcomeCreated = new EventEmitter<{welcomeLang: string, welcomeText: string}>();
newWelcomeLang = '';
newWelcomeText = '';
constructor() { }
ngOnInit() { }
onAddWelcome() {
this.welcomeCreated.emit({
welcomeLang: this.newWelcomeLang,
welcomeText: this.newWelcomeText,
});
}
}
// Dans la template de WelcomeComponent
<app-welcome (welcomeCreated)="onWelcomeAdded($event)"></app-welcome>
<div *ngFor="let welcomeElement of WelcomeElements">
{{ welcomeElement.language }} : {{ welcomeElement.content }}
</div>
// Dans la template de SayHelloComponent
<input type="text" [(ngModel)]="newWelcomeLang">
<input type="text" [(ngModel)]="newWelcomeText">
<button (click)="onAddWelcome()">Add hello</button>
Pour plus d’informations, consultez le documentation officielle.
5. Directives
5.1 Directives d’attribut
On peut modifier l’apparence ou le comportement des éléments du DOM avec les directives, utilisez la commande suivante pour générer une nouvelle directive :
ng generate directive nom-directive
Pour en savoir plus consultez la documentation officielle.
5.2 Directives structurelles
Les directives structurelles permettent de modifier la structure du DOM. Pour en savoir plus, consultez la documentation officielle.
5.3 Moteur de rendu
Le moteur de rendu (Renderer2) permet par exemple de modifier le style d’un élément HTML, consultez la documentation officielle.
5.4 HostListener et HostBinding
La directive HostListener permet d’écouter les événements hôtes. La directive HostBinding permet de se lier aux propriétés de l’hôte. Pour en savoir plus, consultez la documentation officielle.
6. Services injectable
Un service permet de d’inscrire toute la logique d’une fonctionnalité pour une application, cela dans le but de réutiliser plusieurs fois le même code. Pour créer rapidement un nouveau service, on peut utiliser la commande suivante :
ng generate service nom-de-service --skip-tests
Pour en savoir plus, consultez la documentation officielle.
7. Routing
7.1 Configuration d’un Router
Le Router permet à l’utilisateur de se déplacer dans l’application entre les différentes pages. Pour cela, il faut créer un fichier nommé app/app.routes.ts et inscrire un tableau d’itinéraires des routes, comme dans l’exemple suivant :
import { Routes } from '@angular/router';
import {FirstComponent} from './first/first.component';
import {SecondComponent} from './second/second.component';
const routes: Routes = [
{ path: 'first-component', component: FirstComponent },
{ path: 'second-component', component: SecondComponent },
];
Puis dans le fichier app.config.ts, utilisez le code suivant :
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes, withComponentInputBinding()),
]
};
Pour finir, dans la template, utilisez le code suivant :
<a routerLink="/first-component">First component</a>
<a routerLink="/second-component">Second component</a>
Pour en savoir plus, consultez la documentation officielle.
7.2 Router active
On peut ajouter un bout de code qui permet d’ajouter une class si la route est active, comme dans l’exemple suivant :
<p routerLinkActive="active" [routerLinkActiveOptions]="{exact : true}"><a routerLink="/ ">Home component</a></p>
<p routerLinkActive="active"><a routerLink="/first-component">First component</a></p>
<p><a routerLink="/second-component" routerLinkActive="active">Second component</a></p>
7.3 Naviguer
Pour naviguer en dehors d’une template, on utilise le code suivant :
this.router.navigate(['/first-component']);
7.4 Passer des paramètres
On peut passer des paramètres dans une route, pour cela utilisez le code suivant :
// Dans le tableau des routes :
{ path: 'users/:id', component: UsersComponent },
// Dans la template :
<a [routerLink]="[ '/users', user.id ]" routerLinkActive="active">User</a>
Pour récupérer l’id dans le composant UserComponent, on peut utiliser le code suivant :
const userId = this.route.snapshot.params['id'];
Pour en savoir plus, consultez la documentation officielle.
7.5 Query Parameters
Il est possible de récupérer aussi les paramètres passé dans l’URL, comme les ancres, les paramètres GET, pour cela utilisez le code suivant :
<a [routerLink]="[ '/users', 1, 'edit']" [queryParams]="{allowEdit: '1'}" fragment="loading">User</a>
// URL : localhost:4200/users/1/edit?allowEdit=1#loading
this.router.navigate(['/users', 1, 'edit'], {queryParams: {allowEdit: '1'}, fragment: 'loading'});
// URL : localhost:4200/users/1/edit?allowEdit=1#loading
Pour récupérer les paramètres et ancres passés dans l’URL, utilisez le code suivant :
this.route.snapshot.queryParams
this.route.snapshot.fragment
On peut également garder nos paramètres d’un composant à un autre, comme dans l’exemple suivante :
constructor(private route : ActivatedRoute) {}
this.router.navigate(['users', 1, 'edit'], {relativeTo: this.route, queryParamsHandling: 'preserve'});
7.6 Page not found et Redirect
Pour créer une route par défaut pour les URLs qui n’existent pas, on utilise le code suivant :
{ path: 'not-found', component: PageNotFoundComponent },
// Ou
{ path: '**', component: PageNotFoundComponent },
On peut créer des routes pour rediriger un URL vers une autre, comme dans l’exemple suivant :
{ path: '**', redirectTo: '/not-found'},
// Ou
{ path: '', redirectTo: '/home' pathMatch: 'full'},
7.7 Protéger les routes
Pour protéger les routes d’une application, on peut utiliser les guards, pour générer un guard il faut utiliser cette commande :
ng generate guard nom-guard
Pour en savoir plus, consultez la documentation officielle.
7.8 Transmission de données statiques
La propriété data permet de stocker des données liées à une route spécifique, pour cela utilisez le code suivant :
{ path: 'not-found', component: ErrorPageComponent, data: { message: 'Erreur 404 !' } },
Afin de récupérer les données dans le composant ErrorPageComponent, utilisez le code suivant :
errorMessage: string;
this.route.snapshot.data['message'];
// Ou
This.route.data.subscribe(
(data : Data) => {
this.errorMessage = data['message']
}
);
7.9 Enfants et Lazy Loading
On peut créer des routes enfants grâce au code suivant :
const routes: Routes = [
{
path: 'first-component',
component: FirstComponent, // this is the component with the <router-outlet> in the template
children: [
{
path: 'child-a', // child route path
component: ChildAComponent, // child route component that the router renders
},
{
path: 'child-b',
component: ChildBComponent, // another child route component that the router renders
},
],
},
];
Pour en savoir plus sur les routes enfants, consultez la documentation officielle.
On peut implémenter du lazy loading dans les routes grâce au code suivant :
{ path: 'people', component: PeopleComponent },
{ path: 'users-list', loadChildren: './people/users.module#UsersModule' },
Pour en savoir plus sur le lazy loading, consultez la documentation officielle.
8. Formulaires
8.1 Création d’un formulaire
Pour créer un formulaire dans Angular, il faut utiliser ngModel, comme dans l’exemple suivant :
<form (ngSubmit)="onSubmit(f)" #f="ngForm">
<label for="username">Username</label>
<input type="text" id="username" ngModel name="username">
<label for="email">E-mail</label>
<input type="email" id="email" ngModel name="email">
<button type="submit">Submit</button>
</form>
Dans le composant, utilisez le code suivant :
export class AppComponent {
@ViewChild('f') signupForm: NgForm;
onSubmit(form: NgForm) {
console.log(form);
}
}
On peut créer des groupes dans un formulaire grâce au code suivant qui est à mettre sur un div qui regroupe les champs :
<div ngModelGroup="userData">
// Les champs du formulaire...
</div>
Pour créer des boutons radio, on peut utiliser le code suivant :
<div class="radio" *ngFor="let gender of genders">
<label>
<input type="radio" name="gender" ngModel [value]="gender">
{{ gender }}
</label>
</div>
Puis dans le composant, utilisez le code suivant :
genders = ['male', 'female'];
Pour en savoir plus, consultez la documentation officielle.
8.2 Validation de formulaire
Grâce à Angular, on peut ajouter des validations aux inputs HTML, comme dans l’exemple suivant :
<input type="email" id="email" ngModel name="email" required email>
Vous pouvez consulter la liste des validations directement sur la documentation officielle.
On peut désactiver le bouton d’envoi du formulaire si le il n’est pas valide, grâce au code suivant :
<button type="submit" [disabled]="!f.valid">Submit</button>
En cas de champs invalide, on peut personnaliser la class CSS .ng-invalid afin de mettre en valeur les champs incorrects. On peut aussi faire apparaitre un élément si le champ n’est pas valide, comme dans l’exemple suivant :
<input type="email" id="email" ngModel name="email" required email #email="ngModel">
<span *ngIf="!email.valid && email.touched">Email invalid</span>
8.3 Valeur par défaut
Pour définir une valeur par défaut à un sélecteur HTML, on peut attribuer à ngModel la valeur par défaut souhaité, par exemple :
<select id="valid" [ngModel]="yes" name="valid"> // La valeur par défaut peux passer en variable
<option value="yes">Yes</option>
<option value="no">No</option>
</select>
8.4 Liaison bidirectionnelle
Pour effectuer une liaison bidirectionnelle avec Angular, il faut utiliser le code suivant :
<textarea name="message" rows="3" [(ngModel)]="message"></textarea>
<p>Le message : {{ message }}</p>
8.5 Définition et extraction de valeurs
Pour définir la valeur d’un champ dans un formulaire via un action sur un bouton par exemple, on peut utiliser le code suivant :
suggestName() {
const suggestedName = 'John Doe';
// Pour ajouter une valeur à plusieurs inputs
this.signupForm.setValue({
userData : {
username: suggestedName,
email: ''
},
gender: 'male'
});
// Pour modifier une valeur dans un input
this.signupForm.form.patchValue({
userData: {
username: suggestedName
}
});
}
Puis dans la template, on utilise le code suivant :
// Inputs…
<button type="button" (click)="suggestName()">Suggest a name</button>
Pour extraire les données d’un formulaire, on peut utiliser le code suivant :
submitted = false ;
onSubmit() {
this.submitted = true;
this.user.username = this.signupForm.value.userData.username;
this.user.email = this.signupForm.value.userData.email;
this.user.gender = this.signupForm.value.gender;
}
Puis dans la template, on peut utiliser le code suivant :
// Champs du formulaire…
<div *ngIf="submitted">
<p>Username : {{user.username}}</p>
<p>Mail : {{user.email}}</p>
<p>Gender : {{user.gender}}</p>
</div>
Afin de réinitialiser un formulaire (reset), on peut utiliser le code suivant :
onSubmit() {
this.signupForm.reset();
}
8.6 Reactive forms
Les formulaires réactifs (reactive forms) sont basés sur un modèle, ils permettent une gestion des formulaires plus avancé, comme dans l’exemple suivant :
export class AppComponent implements OnInit {
genders = ['male', 'female'];
signupForm: FormGroup;
ngOnInit() {
this.signupForm = new FormGroup({
'username': new FormControl(null),
'email': new FormControl(null),
'gender': new FormControl('male')
});
}
}
Puis dans la template, on peut utiliser le code suivant :
<form [formGroup]="signupForm">
<label for="username">Username</label>
<input type="text" id="username" formControlName="username">
<label for="email">Email</label>
<input type="email" id="email" formControlName="email">
<div class="radio" *ngFor="let gender of genders">
<label>
<input type="radio" formControlName="gender" [value]="gender">
{{ gender }}
</label>
</div>
<button type="submit">Submit</button>
</form>
Pour obtenir les données du formulaire réactif, on utilise le code suivant :
// Dans la template
<form [formGroup]="signupForm" (ngSubmit)="onSubmit()">
// Dans le composant
onSubmit() {
console.log(this.signupForm);
}
Pour la validation des champs du formulaire réactif, on peut utiliser le code suivant :
ngOnInit() {
this.signupForm = new FormGroup({
'username': new FormControl(null, Validators.required),
'email': new FormControl(null, [Validator.required, Validator.email]),
'gender': new FormControl('male')
});
}
Afin d’afficher les messages d’erreurs, on peut utiliser le code suivant :
<label for="email">Email</label>
<input type="email" id="email" formControlName="email">
<span *ngIf="!signupForm.get('email').valid && signupForm.get('email').touched">Please enter a valid email!</span>
Pour créer un groupe de champs dans un formulaire réactif, on peut utiliser le code suivant :
// Dans la template :
<form [formGroup]="signupForm" (ngSubmit)="onSubmit()">
<div formGroupName="userData">
// Formulaire…
</div>
</form>
//Dans le composant :
ngOnInit() {
this.signupForm = new FormGroup({
'userData': new FormGroup({
'username': new FormControl(null, Validators.required),
'email': new FormControl(null, [Validator.required, Validator.email])
}),
'gender': new FormControl('male')
});
}
Pour créer un tableau de champs de formulaire, on peut utiliser le code suivant :
// Dans la template :
<div formArrayName="hobbies">
<button type="button" (click)="onAddHobby()">Add Hobby</button>
<div *ngFor="let hobbyControl of signupForm.get('hobbies').controls; let i = index">
<input type="text" [formControlName]="i">
</div>
</div>
//Dans le composant :
ngOnInit() {
this.signupForm = new FormGroup({
// …
'hobbies': new FormArray([])
});
}
onAddHobby() {
const control = new FormControl(null, Validators.required);
(<FormArray>this.signupForm.get('hobbies')).push(control);
}
Pour créer des validations personnalisées dans un formulaire réactif, on utilise le code suivant :
forbiddenUsernames = ['Chris', 'Anna'];
ngOnInit() {
this.signupForm = new FormGroup({
// …
'username': new FormControl(null, [Validator.required, this.forbiddenNames.bind(this)]),
});
}
forbiddenNames(control: FormControl): {[s: string]: boolean} {
if (this.forbiddenUsernames.indexOf(control.value) !== -1) {
return {'nameIsForbidden': true};
}
return null;
}
On peut également créer nos validations personnalisées dans un fichier séparé nommé custom-validator.ts, comme dans l’exemple suivant :
export class CustomValidators {
static forbiddenNames(control: FormControl): {[s: string]: boolean} {
if (this.forbiddenUsernames.indexOf(control.value) !== -1) {
return {'nameIsForbidden': true};
}
return null;
}
}
// Dans le composant :
ngOnInit() {
this.signupForm = new FormGroup({
// …
'username': new FormControl(null, [Validator.required, CustomValidators.forbiddenNames]),
});
}
Pour afficher un message d’erreur personnalisé, on utilise le code suivant dans la template :
<label for="username">Username</label>
<input type="text" id="username" formControlName="username">
<span *ngIf="!signupForm.get('username').valid && signupForm.get('username').touched">
<span *ngIf="sigupForm.get('userData.username').errors['nameIsForbidden']">This name is invalid!</span>
<span *ngIf="sigupForm.get('userData.username').errors['required']">This field required!</span>
</span>
Pour créer une validation asynchrone, on peut utiliser le code suivant :
ngOnInit() {
this.signupForm = new FormGroup({
// …
'email': new FormControl(null, [Validator.required, Validator.email], this.forbiddenEmails),
});
}
forbiddenEmails(control: FormControl): Promise<any> | Observable<any> {
const promise = new Promise<any>((resolve, reject) => {
setTimeout(() => {
if (control.value === 'test@test.com') {
resolve('emailIsForbidden' : true});
} else {
resolve(null);
}
}, 1500);
});
return promise;
}
Pour écouter les messages d’erreurs d’un formulaire réactif, on utilise le code suivant :
ngOnInit() {
// …
// Affichage des valeurs du formulaire
this.signupForm.valueChanges.subscribe(
(value) => console.log(value)
);
// Affichage du statut des champs du formulaire
this.signupForm.statusChanges.subscribe(
(status) => console.log(status)
);
}
Pour réinitialiser le formulaire réactif, on utilise le code suivant :
onSubmit() {
console.log(this.signupForm);
this.signupForm.reset();
}
Pour en savoir plus sur les formulaires réactifs, consultez la documentation officielle.
9. Pipes
9.1 Utilisation de Pipes
Les Pipes permettent de modifier l’affichage de valeurs dans une template, comme dans l’exemple suivant :
<p>Name : {{ user.name | uppercase }}<p>
<p>Age : {{ user.age | date }}<p>
Voici la liste des Pipes existant :
DatePipe(date) : Formate une valeur de date selon les règles locales.UpperCasePipe(uppercase) : Transforme le texte en majuscules.LowerCasePipe(lowercase) : Transforme le texte en minuscules.CurrencyPipe(currency) : Transforme un nombre en chaîne monétaire, formatée selon les règles locales.DecimalPipe(decimal) : Transforme un nombre en chaîne avec un point décimal, formaté selon les règles locales.PercentPipe(percent) : Transforme un nombre en chaîne de pourcentage, formatée selon les règles locales.AsyncPipe(async) : Abonnez-vous et désabonnez-vous à une source asynchrone telle qu'un observable.JsonPipe(json) : affichez une propriété d'objet de composant à l'écran au formatJSONpour le débogage.
Pour en savoir plus avec les Pipes, consultez la documentation officielle.
On peut passer des paramètres dans les Pipes, comme dans l’exemple suivant :
<p>Age : {{ user.age | date:'fullDate' |uppercase }}<p>
Pour voir la liste des formats de date, consultez la documentation officielle.
9.2 Pipes personnalisés
Pour créer des Pipes personnalisés, il faut ajouter le code suivant dans un fichier différent shorten.pipe.ts :
@Pipe({
name: 'shorten'
})
export class ShortenPipe implements PipeTransform {
transform(value: any, limit: number, cut: string) {
if (value.length > limit) {
return value.substr(0, limit) + cut;
}
return value;
}
}
// Dans la template du composant :
<p>{{ article.title | shorten:15:'…' }}</p>
Pour générer un Pipes personnalisé, on peut utiliser la commande suivante :
ng generate pipe nom_pipe
Pour en savoir plus, consultez la documentation officielle.
9.3 Pure Pipe
La propriété pure d’un Pipe permet de forcer la mise à jour du Pipe à chaque fois que les données changent. Par défaut cette propriété est a true, il faut la mettre à false, comme dans l’exemple suivant :
@Pipe({
name: 'filter',
pure: false
})
Pour en savoir plus, consultez la documentation officielle.
10. Http Requests
Pour voir le guide complet sur Http Client de Angular, consultez la documentation officielle.
10.1 POST Request
Pour effectuer une requête POST avec Angular, il faut utiliser HttpClient comme dans l’exemple suivant :
constructor(private http: HttpClient) {}
onCreatePost(postData: { title: string; content: string }) {
// Ou : onCreatePost(postData: Post) {
this.http.post('https://labonnealternance.apprentissage.beta.gouv.fr/api/v1/jobs/establishment',
// Ou : this.http.post<{ name: string }>('https://labonnealternance.apprentissage.beta.gouv.fr/api/v1/jobs/establishment',
postData
).subscribe(responseData => {
console.log(responseData);
});
}
Pour en savoir plus, consultez la documentation officielle.
10.2 GET Request
Pour effectuer une requête GET avec Angular, il faut utiliser le code suivant :
loadedPosts = [];
// Ou : loadedPosts: Post[] = [];
constructor(private http: HttpClient) {}
ngOnInit() {
this.fetchPosts();
}
onFetchPosts() {
this.fetchPosts();
}
private fetchPosts() {
this.http.get('https://labonnealternance.apprentissage.beta.gouv.fr/api/v1/metiers/all')
.subscribe(posts => {
console.log(posts);
this.loadedPosts = posts;
});
}
On peut formater les données reçu, grâce au code suivant :
private fetchPosts() {
this.http.get('https://labonnealternance.apprentissage.beta.gouv.fr/api/v1/metiers/all')
// Ou : this.http.get<{ [key: string]: Post }>('https://labonnealternance.apprentissage.beta.gouv.fr/api/v1/metiers/all')
.pipe(
map(responseData => {
const postsArray = [];
// Ou : const postsArray: Post[] = [];
for (const key in responseData) {
if (responseData.hasOwnProperty(key)) {
postsArray.push({ …responseData[key], id: key });
}
}
return postsArray;
})
)
.subscribe(posts => {
console.log(posts);
});
}
On peut créer un indicateur de chargement facilement, en utilisant le code suivant :
<p *ngIf="isFetching">Loading…</p>
10.3 Utilisation d’un service
On peut déplacer le code du fetch directement dans un service afin que cela soit réutilisable, comme dans l’exemple suivant :
// Dans le composant :
loadedPosts: Post[] = [];
isFetching = false;
constructor(private http: HttpClient, private postsService: PostsService) {}
ngOnInit() {
this.isFetching = true;
this.postsService.fetchPosts().subscribe(
posts => {
this.isFetching = false;
this.loadedPosts = posts ;
}
);
}
onCreatePost(postData: Post) {
this.postsService.createAndStorePost(postData.title, postData.content);
}
onFetchPosts() {
this.isFetching = true;
this.postsService.fetchPosts().subscribe(
posts => {
this.isFetching = false;
this.loadedPosts = posts ;
}
);
}
// Dans le service posts.service.ts :
@Injectable({providedIn: 'root'})
export class PostsService {
constructor(private http: HttpClient) {}
createAndStorePost(title: string, content: string) {
const postData : Post = { title: title, content: content };
// Requête post…
}
fetchPosts() {
return this.http.get<{ [key: string]: Post }>('https://labonnealternance.apprentissage.beta.gouv.fr/api/v1/metiers/all')
.pipe(map(responseData => {
const postsArray: Post[] = [];
for (const key in responseData) {
if (responseData.hasOwnProperty(key)) {
postsArray.push({ …responseData[key], id: key });
}
}
return postsArray;
})
);
}
}
10.4 DELETE Request
Pour effectuer une requête DELETE avec Angular, il faut utiliser le code suivant :
// Dans le composant :
onClearPosts() {
this.postsService.deletePosts().subscribe(() => {
this.loadedPosts = [];
})
}
// Dans posts.service.ts :
deletePosts() {
return this.http.delete('https://labonnealternance.apprentissage.beta.gouv.fr/api/v1/metiers/delete');
}
10.5 Gestion des erreurs
Pour afficher les erreurs lors d’une requête, il faut utiliser le code suivant :
// Dans le composant :
error = null;
ngOnInit() {
this.isFetching = true;
this.postsService.fetchPosts().subscribe(
posts => {
this.isFetching = false;
this.loadedPosts = posts ;
},
error => {
this.isFetching = false;
this.error = error.message;
}
);
}
onFetchPosts() {
this.isFetching = true;
this.postsService.fetchPosts().subscribe(
posts => {
this.isFetching = false;
this.loadedPosts = posts ;
},
error => {
this.isFetching = false;
this.error = error.message;
}
);
}
// Dans la template du composant :
<p *ngIf="isFetching && !error">Loading…</p>
<div *ngIf="error">
<h1>An Error Occurred!</h1>
<p>{{ error }}</p>
</div>
Il existe une autre méthode pour afficher des messages d’erreurs, il s’agit de Subjects comme dans l’exemple suivant :
// Dans le composant :
export class AppComponent implements ngOnInit, OnDestroy {
private errorSub: Subscription;
ngOnInit() {
this.errorSub = this.postsService.error.subscribe(errorMessage => {
this.error = errorMessage;
});
// …
}
ngOnDestroy() {
this.errorSub.unsubscribe();
}
// …
}
// Dans posts.service.ts :
error = new Subject<string>();
createAndStorePost(title: string, content: string) {
const postData : Post = { title: title, content: content };
this.http.post<{ name: string }>('https://labonnealternance.apprentissage.beta.gouv.fr/api/v1/jobs/establishment',
postData
).subscribe(
responseData => {
console.log(responseData);
},
error => {
this.error.next(error.message);
}
);
}
fetchPosts() {
return this.http.get<{ [key: string]: Post }>('https://labonnealternance.apprentissage.beta.gouv.fr/api/v1/metiers/all')
.pipe(
map(responseData => {
const postsArray: Post[] = [];
for (const key in responseData) {
if (responseData.hasOwnProperty(key)) {
postsArray.push({ …responseData[key], id: key });
}
}
return postsArray;
}),
catchError(errorRes => {
return throwError(errorRes);
})
);
}
10.6 Paramètres de requête
Pour paramétrer les Headers d’une requête, il faut utiliser le code suivant :
this.http.get(
'https://labonnealternance.apprentissage.beta.gouv.fr/api/v1/metiers/all',
{
headers: new HttpHeaders({ 'Custom-Header': 'Hello' })
}
);
Pour passer des paramètres en GET dans l’URL lors d’une requête à une API, on utilise le code suivant :
let searchParams = new HttpParams();
searchParams = searchParams.append('print', 'pretty');
searchParams = searchParams.append('custom', 'key');
this.http.get(
'https://labonnealternance.apprentissage.beta.gouv.fr/api/v1/metiers/all',
{
headers: new HttpHeaders({ 'Custom-Header': 'Hello' }),
params : searchParams
}
);
On peut également observer les différents types de réponses, comme dans l’exemple suivant :
this.http.post<{ name: string }>(
'https://labonnealternance.apprentissage.beta.gouv.fr/api/v1/jobs/establishment',
postData,
{
observe: 'response'
}
);
this.http.delete(
'https://labonnealternance.apprentissage.beta.gouv.fr/api/v1/metiers/delete',
{
observe: 'events'
}
).pipe(
tap(event => {
console.log(event);
if (event.type === HttpEventType.Sent) {
// …
}
if (event.type === HttpEventType.Response) {
console.log(event.body);
}
})
);
On peut aussi changer le format des réponses, grâce au code suivant :
this.http.get(
'https://labonnealternance.apprentissage.beta.gouv.fr/api/v1/metiers/all',
{
headers: new HttpHeaders({ 'Custom-Header': 'Hello' }),
params : searchParams,
responseType: 'json'
}
);
10.7 Intercepteurs
Les intercepteurs (interceptors) sont des fonctions qui d’exécutent à chaque requête, c’est utile pour l’authentification, actualisation de résultats, mise en cache ou mesure du temps de réponse par exemple. Voici le code de l’intercepteur dans le service auth-interceptor.service.ts :
export class AuthInterceptorService implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler) {
console.log('Request is on its way');
return next.handle(req);
}
}
Puis dans le fichier app.module.ts, il faut ajouter le code suivant :
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, FormsModule, HttpClientModule],
providers: [
{
provide: HTTP_INTERCEPTORS,
userClass: AuthInterceptorService,
multi: true
}
],
Bootstrap : [AppComponent]
});
export class AppModule {}
Pour modifier un l’objet d’une requête, utilisez le code suivant :
intercept(req: HttpRequest<any>, next: HttpHandler) {
console.log(req.url);
const modifiedRequest = req.clone({
headers: req.headers.append('Auth', 'xyz')
});
return next.handle(modifiedRequest);
}
Pour envoyer une réponse grâce à l’intercepteur, il faut utiliser le code suivant :
intercept(req: HttpRequest<any>, next: HttpHandler) {
const modifiedRequest = req.clone({
headers: req.headers.append('Auth', 'xyz')
});
return next.handle(modifiedRequest).pipe(
tap(event => {
console.log(event);
if (event.type === HttpEventType.Response) {
// Response arrived body data
console.log(event.body);
}
})
);
}
Pour en savoir plus sur les intercepteurs, consultez la documentation officielle.
10.8 Authentification JWT
L'authentification permet de sécuriser une application Angular, pour en savoir plus, consultez la documentation officielle.
11. Local Storage
Le stockage local (local storage) permet d'enregistrer des informations du côté client, il n'est pas effacé après une session.
localStorage.setItem(key, value): Permet d'ajouter une nouvelle valeur en stockage local.localStorage.getItem(key): Permet d'obtenir une valeur en stockage local.localStorage.removeItem(key): Permet de supprimer une valeur en stockage local.localStorage.clear(): Permet de supprimer toutes les valeurs en stockage local.
12. Modules
Les modules (NgModules) permettent d'organiser une application Angular, pour en savoir plus, consultez la documentation officielle.
13. Signaux
Les signaux (signals) permettent de transmettre l'information de changement de valeur, pour en savoir plus sur les signaux, consultez la documentation officielle.
14. Server-side rendering
Le rendu côté serveur (Server-Side Rendering) permet le rendu des pages sur le serveur, ce qui signifie que le contenu HTML de la page est directement envoyé au navigateur. L'application Angular est pré-rendue sur le serveur et le code HTML terminé est envoyé au client. Par la suite, il redevient un SPA (Single Page Application) côté client. Pour en savoir plus, consultez la documentation officielle.
15. Animations
Les animations permettent de donner l'illusion de mouvement à des éléments HTML, grâce à un changement de style au cours du temps. Pour en savoir plus sur les animations, consultez la documentation officielle.
16. Service worker
Le Service worker permet de mettre en cache une application Angular, afin que celle-ci soit accessible même sans connexion. Pour en savoir plus sur les service worker, consultez la documentation officielle.
17. Tests
Les tests permettent de vérifier que l'application Angular fonctionne correctement, pour en savoir plus sur les tests, consultez la documentation officielle.
18. Création d’une interface
Pour créer rapidement une nouvelle interface, on peut utiliser la commande suivante :
ng generate interface nom-de-interface
Pour en savoir plus sur les interfaces, consultez la documentation officielle.
19. Dépendances et Outils
19.1 JSON Server
On peut utiliser un serveur JSON (JSON Server) pour créer une API REST, cela est très pratique pour ce créer une API de test pour le développement. Il faut utiliser la commande suivante pour installer un serveur JSON :
npm install -g json-server
{
"locations": [
{
"id": 1,
"name": "a",
},
{
"id": 2,
"name": "b",
},
]
}
Puis dans le répertoire racine du projet Angular, on doit créer un fichier nommé db.json et y stocker les données souhaités au format JSON. Une fois cela fait on peut lancer l'API REST via la commande suivante :
json-server --watch db.json
Pour en savoir plus sur la création de serveur JSON, consultez la documentation officielle.
19.2 Angular Material
Angular Material est une bibliothèque de composants pour l'interface utilisateur. Pour en savoir plus sur Angular Material, consultez la documentation officielle.
19.3 Bootstrap
Pour ajouter Bootstrap au projet Angular, il faut utiliser cette commande :
npm install @ng-bootstrap/ng-bootstrap@next --legacy-peer-deps
// ou
ng add @ng-bootstrap/ng-bootstrap
Pour en savoir plus sur Bootstrap avec Angular, consultez la documentation officielle.
19.4 NgRx
NgRx est un framework pour créer des applications réactives en Angular. NgRx fournit des bibliothèques pour gérer l'état global et local, la gestion des collections d'entités, l'intégration avec le routeur angulaire... Pour en savoir plus sur NgRx, consultez la documentation officielle.