J’aime beaucoup Vue.js, mais apprendre un autre framework JavaScript peut m’être utile dans les missions auxquelles je participe.
J’ai décidé d’approfondir mes connaissances d’Angular grâce à un cours YouTube de 4 heures.
Le cours est basé sur Angular 17, mais j’ai travaillé avec Angular 19 au moment où je l’ai suivi, ce qui m’a permis de me familiariser avec l’API plus récente fournie par Angular.
Installation de l’environnement de développement
Installer NodeJS
Pour Windows, utilisez Scoop :
|
|
Remarque : installez la version LTS pour éviter les messages « Avertissement : la version actuelle de Node (23.9.0) n’est pas prise en charge par Angular » provenant d’Angular à l’étape suivante.
Installer Angular
|
|
IMPORTANT : Au moment où j’ai écrit ces lignes, Angular était à la version 19, alors que le cours a été dispensé dans Angular 17 et 18.
Extensions VSCode
Personnellement, j’utilise les extensions suivantes :
- https://marketplace.visualstudio.com/items?itemName=1tontech.angular-material
- https://marketplace.visualstudio.com/items?itemName=alexiv.vscode-angular2-files
- https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss
- https://marketplace.visualstudio.com/items?itemName=christian-kohler.path-intellisense
- https://marketplace.visualstudio.com/items?itemName=cyrilletuzi.angular-schematics
- https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint
- https://marketplace.visualstudio.com/items?itemName=editorconfig.editorconfig
- https://marketplace.visualstudio.com/items?itemName=formulahendry.auto-close-tag
- https://marketplace.visualstudio.com/items?itemName=formulahendry.auto-rename-tag
- https://marketplace.visualstudio.com/items?itemName=gruntfuggly.todo-tree
- https://marketplace.visualstudio.com/items?itemName=humao.rest-client
- https://marketplace.visualstudio.com/items?itemName=infinity1207.angular2-switcher
- https://marketplace.visualstudio.com/items?itemName=john-crowson.angular-file-changer
- https://marketplace.visualstudio.com/items?itemName=loiane.angular-extension-pack
- https://marketplace.visualstudio.com/items?itemName=obenjiro.arrr
- https://marketplace.visualstudio.com/items?itemName=patbenatar.advanced-new-file
- https://marketplace.visualstudio.com/items?itemName=pucelle.vscode-css-navigation
- https://marketplace.visualstudio.com/items?itemName=quicktype.quicktype
- https://marketplace.visualstudio.com/items?itemName=rctay.karma-problem-matcher
- https://marketplace.visualstudio.com/items?itemName=segerdekort.angular-cli
- https://marketplace.visualstudio.com/items?itemName=simontest.simontest
- https://marketplace.visualstudio.com/items?itemName=steoates.autoimport
- https://marketplace.visualstudio.com/items?itemName=stringham.move-ts
- https://marketplace.visualstudio.com/items?itemName=techer.open-in-browser
- https://marketplace.visualstudio.com/items?itemName=wix.vscode-import-cost
Créer le projet
|
|
Voir mes notes concernant le dossier assets supprimé sous src.
Les composants
Pour créer un composant, utilisez cette commande :
|
|
Ce qui précède crée un nouveau sous-dossier components/playing-card sous app. Le composant échafaudé est défini avec les fichiers .css, .html, ts et spec.ts.
Pour ignorer la génération des fichiers de test, il suffit d’ajouter le drapeau --skip-tests à la commande ci-dessus.
Entrées et signaux d’entrée
La première manière de déclarer des entrées utilise le décorateur :
|
|
Vous pouvez ensuite l’utiliser dans le composant parent qui l’utilise :
|
|
Cependant, j’ai appris que, si vous définissez une entrée avec un type différent d’une chaîne, alors vous devez utiliser le [ ].
Par exemple :
|
|
Il peut également contenir une simple expression JavaScript :
|
|
De même, nous pouvons transmettre des objets :
|
|
Pour fonctionner, vous devriez utiliser la bonne pratique de créer une classe model que vous utilisez pour initialiser pik et définir le type d’entrée de app-playing-card avec le nom de la classe.
Par exemple, je pourrais avoir cette classe :
|
|
Je l’utilise pour déclarer pik dans mon app.component :
|
|
A propos du `!`
Le ! est l’opérateur d’assertion de l’assignation définie.
Il indique au compilateur TypeScript « Je sais que cette propriété semble ne pas être initialisée, mais faites-moi confiance, une valeur lui sera attribuée avant qu’elle ne soit utilisée ».
Sans lui, TypeScript émettrait une erreur, car l’objet pik est déclaré, mais n’est pas immédiatement initialisé lors de la déclaration. Puisque vous l’assignez au constructeur, le ! supprime cette erreur.
Et je l’utilise pour saisir les données du composant de mon enfant :
|
|
Une autre caractéristique du décorateur @Input est sa configuration : vous pouvez faire en sorte que l’entrée de la carte soit requise :
|
|
Et TypeScript vous dira ce qui suit :
Vous pouvez personnaliser le nom de l’attribut avec alias ou transformer votre objet d’entrée avec transform, mais je ne vois pas de bon cas d’utilisation pour donner un exemple pour cela.
Maintenant, depuis Angular 17, vous pouvez utiliser les signaux d’entrée de la manière suivante :
|
|
Dans le code HTML, vous devrez ajouter () pour accéder aux propriétés de l’entrée.
|
|
Sorties, signaux de sorties et modèles
Lorsque nous avons besoin de communiquer des données d’un composant enfant à son parent, nous pouvons utiliser le décorateur @Output. Nous l’appelons aussi événement émis.
Un exemple très simple ressemblerait à ceci :
|
|
Dans le fichier HTML du composant de votre enfant, vous pouvez ajouter le searchClick à un bouton :
|
|
Ensuite, dans le fichier TS de votre composant parent, vous ajoutez une propriété searchClickCount initialisée à 0 et vous pouvez écouter les événements et imprimer la valeur searchClickCount mise à jour.
|
|
Souvent, nous transmettons les données vers le haut de la hiérarchie des composants, ce qui n’est pas le cas dans l’exemple ci-dessus.
Disons que nous voulons afficher le terme recherché dans le composant parent.
Nous devons d’abord mettre à jour le fichier TS du composant enfant :
|
|
Ensuite, nous mettons à jour le fichier HTML du composant enfant pour utiliser searchTerm avec la directive [ngModel] :
|
|
Enfin, nous ajoutons la propriété searchedTerm = $event au composant parent pour compléter la liaison bidirectionnelle des données et nous terminons par la mise à jour du fichier HTML du composant parent :
|
|
Cependant, nous pouvons raccourcir ce code comme suit :
|
|
Ceci n’est possible que parce que la sortie dans le composant enfant est nommée comme l’entrée avec le préfixe Change. Angular vous indiquera si vous utilisez la version courte de manière incorrecte.
De la même manière que pour les entrées, à partir d’Angular 17.3, vous pouvez utiliser la nouvelle méthode avec les signaux de sortie :
|
|
Tout le reste du code ne change pas.
Depuis Angular 17.2, vous pouvez également simplifier encore plus le code en utilisant model. Dans votre fichier TS, le composant enfant deviendrait :
|
|
Dans le code HTML, vous pouvez modifier le code comme suit grâce à la liaison bidirectionnelle des données avec la directive [(ngModel)] :
|
|
Vous pouvez ainsi supprimer la méthode updateSearch.
Détecter les changements
Angular a plusieurs méthodes de cycle de vie sur les composants qui vous aident à initialiser un composant et à exécuter la logique sur les changements.
Vous pouvez trouver la liste complète dans la documentation.
Stratégie par défaut vs OnPush
Par défaut, lorsqu’un événement est déclenché sur un composant, Angular vérifie toute l’arborescence du composant.
Cela peut causer des problèmes de performance sur une grande application.
C’est alors que vous pouvez utiliser la stratégie OnPush. Cela limite Angular à ne vérifier les composants OnPush que lorsque leurs entrées changent, qu’un événement se produit en leur sein, ou qu’un observable lié émet - en sautant les vérifications inutiles dans le cas contraire.
Sergio, auteur du cours que j’ai suivi, l’explique très bien dans cette section du cours. Si vous ne parlez pas français, activez la fonction de traduction automatique des sous-titres sur YouTube.
Signaux dans Angular
Venant de Vue.js, j’ai l’impression qu’Angular a pris le meilleur de la réactivité de Vue avec les primitives de signaux que vous pouvez utiliser à partir d’Angular 16 :
signal()≈ref()dans Vue, les deux créent des primitives réactives.computed(() => {}): est-ce un copier-coller de Vue ? En tout cas, le fonctionnement et la syntaxe sont identiques.effect()≈watchEffect()(pas watch) qui s’exécute automatiquement lorsque les dépendances changent.
Par exemple, disons que nous stockons un index sélectionné dans un signal et un objet dans un computed qui change lorsque l’index change :
|
|
Ensuite, dans le HTML, nous pouvons changer :
|
|
to
|
|
L’utilisation de signaux rend le code plus propre et plus court. Par exemple, le TypeScript était le suivant en utilisant la méthode historique :
|
|
Avec les signaux et en gardant la même fonctionnalité, nous avons :
|
|
Pourquoi les signaux alors ?
Finalement, Angular abandonnera la détection des changements, réalisée actuellement avec la bibliothèque zone.js, et utilisera les signaux pour vérifier seulement les composants dont nous avons besoin sur les événements.
Cela améliorera grandement les performances des grandes applications !
Boucles et conditions
Utilisation des boucles
Rien d’extraordinaire pour une utilisation simple. Assurez-vous d’importer CommonModule dans votre fichier TS et utilisez la directive *ngFor comme suit :
|
|
Utilisation des conditions
Avec les conditions, nous pouvons utiliser différentes approches :
-
Plusieurs directives
*ngIf.1 2 3 4 5 6 7<p *ngIf="filteredCards().length === 0" style="text-align: center"> No card found </p> <p *ngIf="filteredCards().length > 0" style="text-align: center"> Found {{ filteredCards().length }} card{{ filteredCards().length > 1 ? "s" : "" }}! </p> -
un seul
*ngIfavec un élément<ng-template>. Cela me fait penser aux slots, mais ce n’est pas la même chose.1 2 3 4 5 6 7 8 9 10 11 12<p *ngIf="filteredCards().length === 0; else resultsFound" style="text-align: center" > No cards found </p> <ng-template #resultsFound> <p style="text-align: center"> Found {{ filteredCards().length }} card{{ filteredCards().length > 1 ? "s" : "" }}! </p> </ng-template> -
un seul
*ngIfavec plusieurs éléments<ng-template>.1 2 3 4 5 6 7 8 9 10 11 12 13<p *ngIf="filteredCards().length === 0; then empty; else resultsFound" style="text-align: center" ></p> <ng-template #empty> <p style="text-align: center">No cards found</p> </ng-template> <ng-template #resultsFound> <p style="text-align: center"> Found {{ filteredCards().length }} card{{ filteredCards().length > 1 ? "s" : "" }}! </p> </ng-template>
Nouvelle syntaxe pour les conditions
Avec Angular 17, la nouvelle syntaxe a apporté la possibilité d’écrire un code plus propre et plus clair.
Si nous reprenons le dernier exemple ci-dessus avec la condition, nous pourrions maintenant écrire :
|
|
De même, nous réécrivons la boucle :
|
|
Que signifie track ? Angular l’utilise pour tracer les mises à jour du DOM au minimum lorsque les données changent.
En ce qui concerne @for, vous pouvez l’utiliser avec @empty' de sorte que notre code précédent le @if…@else` devient :
|
|
@for fournit quelques variables supplémentaires que vous pouvez utiliser : $index, $first, $last, $odd, $event et $count. Lisez la documentation pour plus de détails.
Si vous deviez coder des @for imbriqués, l’accès à ces variables intégrées pourrait devenir délicat. Vous pouvez nommer chaque variable d’un @for après le track comme suit :
|
|
The same exists on *ngFor but you must declare a local variable if you need to use them. Please read the documentation for more information on the topic.
Services
Un service dans Angular permet de séparer la logique de l’interface utilisateur des données et de la logique commerciale.
Nous utilisons les services comme des singletons injectables.
Vous pouvez créer le service en utilisant le CLI :
|
|
Avant Angular 14, vous pouviez injecter des services dans les composants à travers les constructeurs, d’où le nom Constructor Dependency Injection que de nombreux ingénieurs logiciels utilisent lors de la mise en œuvre des principes S.O.L.I.D.
|
|
Cependant, cette méthode rend l’héritage complexe. Au lieu de cela, vous pouvez maintenant utiliser la nouvelle méthode inject :
|
|
Les services Angular relient les composants à la source de données, quelle qu’elle soit pour votre application.
Il peut contenir des méthodes CRUD ou toute logique métier permettant de préparer les données pour vos composants.
Par exemple :
|
|
Routes
Cela ressemble beaucoup à Vue Router, bien que, depuis que j’ai suivi Masterclass 2024 de VueSchool, j’aime davantage l’approche de Nuxt avec Unplugin Vue Router qui utilise un routage basé sur l’arborescence de fichiers.
Ajouter une route
Le processus de création de l’application vous fournit un fichier app.routes.ts qui reste vide par défaut.
Vous ajoutez une route avec ce qui suit :
|
|
Ensuite, vous ajoutez à import : [] sur le fichier app.component.ts le RouterOutlet pour l’ajouter au fichier app.component.html :
|
|
Si vous avez besoin de rediriger un chemin vers une autre route, disons / vers /home, vous pouvez définir la route comme suit :
|
|
Qu’en est-il de la gestion des routes inconnues ?
|
|
Cependant, tout comme avec Vue Router, l’ordre a de l’importance, alors mettez cette route en bas de la liste… 😁
Gestion des paramètres d’une route
Encore une fois, c’est très similaire à Vue Router :
|
|
Mais qu’en est-il si vous avez des routes similaires ? Par exemple, la précédente et celle qui suit :
|
|
Vous pouvez les regrouper de la manière suivante :
|
|
Comment lire les paramètres d’une route ?
En prenant la route /card/:id définie précédemment, vous devez charger la route courante en injectant la route ActivatedRoute dans le composant cible CardComponent :
|
|
Avec cette variable route, vous pouvez extraire du tableau params l’identifiant id :
|
|
Vous pouvez ensuite utiliser le signal cardId dans le fichier HTML.
Comment naviguer vers une route ?
Prenons un exemple avec un bouton de navigation « Next » sur la route /card/:id. Nous voulons incrémenter le cardId à chaque clic.
La méthode next() que nous utiliserons dans le fichier HTML ressemblera à ceci :
|
|
Mais… vous remarquerez peut-être un problème. Lorsque vous cliquez sur le premier suivant, la route change, mais pas le code HTML. Et si vous cliquez à nouveau, rien ne change.
Pourquoi ?
Parce que nous utilisons un snapshot de params et qu’il n’est pas suivi. De plus, Angular n’exécute pas le ngOnInit à nouveau. Par conséquent, cardId n’est pas mis à jour.
Pour résoudre ce problème, j’ai fait allusion à la solution : nous devons nous abonner (sujet détaillé plus en détails ci-dessous) au changement de params de la route.
Je vais fournir la solution dans l’extrait de code suivant :
|
|
Avec ce code, le HTML est mis à jour et vous pouvez cliquer sur suivant à l’infini.
Formulaires réactifs
Nous avons plusieurs types de gestion de formulaires dans Angular :
- Template Driven Forms : avec cette méthode, nous utilisons la liaison bidirectionnelle des données avec
ngModeldans le fichier HTML. - Formulaires réactifs : avec cette méthode, le comportement du formulaire est déclaré dans le fichier TS.
Examinons la méthode Formulaires réactifs.
Les bases
Pour commencer, nous devons ajouter le module ReactiveFormsModule au fichier TS pour commencer à ajouter un nouveau FormControl qui représente les entrées individuelles :
|
|
La valeur par défaut est required et vous pouvez ajouter des validateurs en utilisant la classe Validators fournie par Angular Forms.
Pendant que nous sommes dans le fichier TS, ajoutons la méthode submit qui reçoit les données soumises :
|
|
Maintenant, dans le fichier HTML, nous pouvons déclarer le nouveau formulaire :
|
|
Dans le code ci-dessus, de bas en haut :
- le bouton
submitest désactivé tant que les deux champs ne sont pas valides. - pour chaque champ, nous lions le contrôle de formulaire à l’entrée
- l’élément de formulaire lie la méthode
submità l’événementsubmit.
Vous voudrez probablement me dire qu’il n’est pas pratique de vérifier chaque champ sur le bouton submit. C’est là que FormGroup entre en scène !
Utilisation de FormGroup
Pour rendre l’article plus court, je vais limiter l’exemple à deux champs :
-
Dans le fichier TS, vous définissez le groupe de formulaires :
1 2 3 4 5 6 7 8form = new FormGroup({ name: new FormControl("", [Validators.required]), hp: new FormControl(0, [ Validators.required, Validators.min(1), Validators.max(200), ]), }); -
Dans le fichier HTML, vous devez adapter quelques éléments :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21<!-- first, add `[formGroup]="form"` to the form element --> <form [formGroup]="form" (submit)="submit($event)"> <div class="form-field"> <label for="name">Name</label> <!-- next, change `[formControl]` to `formControlName` --> <input id="name" name="name" type="text" formControlName="name" /> <!-- the method `isFieldValid` groups the logic of the previous to avoid repetition --> @if (isFieldValid("name")) { <p class="error">This field is required</p> } </div> <div class="form-field"> <label for="hp">HP</label> <input id="hp" name="hp" type="number" formControlName="hp" /> @if (isFieldValid("hp")) { <p class="error">This field is invalid</p> } </div> <!-- finally, we can use `form.invalid` to check form's validity --> <button type="submit" [disabled]="form.invalid">Save</button> </form>
Utilisation du service `FormBuilder
Pour simplifier le code TypeScript, nous pouvons également utiliser le service FormBuilder.
Pour cela, commençons par l’importer :
|
|
Ensuite, vous pouvez réfracter le groupe de formulaires comme suit :
|
|
Construire une entrée de sélection
Si vous avez besoin d’une liste déroulante, vous devez :
-
déclarer la source des données.
-
ajouter l’élément
selectavec une boucleforpour ajouter chaque option.1 2 3 4 5 6<label for="type">Type</label> <select id="type" name="type" type="number" formControlName="type"> @for (type of cardTypes; track type) { <option [value]="type.Id">{{ type.Name }}</option> } </select>
Gestion des entrées de fichiers
Dans le cas d’une champ de type fichier, vous devrez gérer le changement de fichier avec une méthode personnalisée. Le HTML écoute sur l’événement (change) :
|
|
Et la fonction onFileChange se charge de mettre à jour le champ du formulaire cible :
|
|
Gérer des validateurs multiples
La façon la plus simple d’y parvenir est la suivante :
|
|
Angular Material
Angular Material est la bibliothèque de composants d’interface utilisateur officielle de Google pour Angular. Elle met en œuvre les principes du Material Design.
Vous pouvez l’installer avec la commande suivante :
|
|
Et vous devrez répondre à certaines questions :
|
|
Ensuite, rendez-vous sur Angular Material website pour faire votre choix de composants.
Dans l’exemple précédent, nous pouvons mettre à jour les entrées text, number et select.
Pour ce faire, nous devons
-
importer les modules nécessaires :
1 2 3 4 5 6 7 8imports: [ /* Les modules ou composants existants */ /* ... */ /* Et ajoutez ce qui suit pour les composants matériels */ MatButtonModule, MatInputModule, MatSelectModule, ], -
pour intégrer les modules dans le code HTML :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24<!-- replaces the `<div class="form-field">` --> <mat-form-field> <!-- replaces the `<label>` --> <mat-label for="name">Name</mat-label> <!-- add the `matInput` attribute --> <input matInput id="name" name="name" type="text" formControlName="name" /> @if (isFieldValid("name")) { <!-- replaces the `<p class="error">` --> <mat-error>This field is required</mat-error> } </mat-form-field> <mat-form-field> <mat-label for="type">Type</mat-label> <!-- replaces the `<select>` --> <mat-select id="type" name="type" type="number" formControlName="type"> @for (type of cardTypes; track type) { <!-- replaces the `<option>` --> <mat-option [value]="type">{{ type }}</mat-option> } </mat-select> @if (isFieldValid("type")) { <mat-error>This field is invalid</mat-error> } </mat-form-field>
Maintenant, Angular Material ne fournit aucun composant pour l’entrée de fichiers. Sergio prend l’option intelligente d’afficher un bouton pour simuler le clic sur « Choose file » tout en cachant l’entrée de type file.
|
|
Ensuite, nous implémentons la méthode de mise à jour du bouton :
|
|
Gestion de l’authentification
Introduction
Nous devons commencer par ajouter un fournisseur, puisque l’authentification nécessitera l’utilisation d’une API REST à travers un client HTTP.
Pour cela, ajoutons ce fournisseur à app.config.ts :
|
|
Créer le AuthService
L’étape suivante consiste à créer un nouveau AuthService et à importer le HttpClient pour l’utiliser dans votre nouveau service :
|
|
Ensuite, nous ajoutons la propriété user au service, qui sera un signal de type User | null | undefined.
|
|
En matière d’authentification, nous avons généralement besoin
- d’une méthode
loginqui reçoit les informations d’identification. - d’une méthode
logoutqui met fin à la session. - d’une méthode
getUserqui récupère les informations de l’utilisateur.
Codons leur signature :
|
|
Nous devons ajouter l’interface ICredentials :
|
|
Et le modèle User :
|
|
Nous continuons avec l’appel à la méthode de connexion de l’API. L’API de Sergio utilise une méthode de connexion qui renvoie un jeton que nous devons stocker en local afin de pouvoir l’utiliser ultérieurement.
|
|
Pourquoi y a-t-il trois types de méthodes login et getUser ?
undefinedidentifie le cas d’utilisation « Nous ne savons pas encore si l’utilisateur est connecté ».nullidentifie le cas d’utilisation « Nous savons que l’utilisateur n’est pas connecté ».Useridentifie le cas d’utilisation « L’utilisateur est connecté ».
Voici maintenant l’implémentation des méthodes getUser et logout :
|
|
PS : La méthode tap est un opérateur RxJS qui effectue des effets (comme la journalisation ou le débogage) sans modifier les valeurs émises dans un flux observable.
Utiliser le AuthService sur le composant de connexion
Tout d’abord, nous devons injecter les dépendances :
- le nouveau
AuthServicepour utiliser la méthodelogin. - le routeur pour gérer la navigation si l’action de connexion est réussie.
|
|
Ensuite, implémentons la méthode login :
|
|
Si vous vous interrogez sur le logout, c’est très simple :
|
|
Cependant, vous avez peut-être remarqué que le point de terminaison logout ne prend aucun paramètre. Alors, comment dire à l’API REST qui se déconnecte ?
Intercepteurs
Les intercepteurs permettent de modifier une requête HTTP pour ajouter, par exemple, un en-tête HTTP.
C’est ce que nous devons faire si nous voulons appeler l’API REST. En effet, elle attend le jeton reçu lors de la connexion.
Pour créer un nouvel intercepteur, exécutez la commande Angular ci-dessous :
|
|
Dans le système d’authentification fourni par Sergio, le backend requiert un en-tête HTTP Authorization : Token {valeur du Token}.
L’intercepteur agit comme un proxy pour ajouter des données aux requêtes HTTP, dans notre cas un en-tête HTTP :
|
|
Désormais, toute requête à l’API REST reçoit le jeton dans l’en-tête et le point de terminaison logout peut le récupérer pour déconnecter la session associée.
Il reste une dernière étape pour terminer tout ce travail : indiquer au client HTTP comment utiliser l’intercepteur que nous avons créé.
Nous le faisons en mettant à jour le provideHttpClient pour qu’il l’utilise :
|
|
Nous avons presque terminé ! La dernière chose à coder est d’empêcher les utilisateurs de voir les pages nécessitant un statut « authentifié ».
Gardes
Les gardes nous aideront pour la dernière partie.
Les gardes fonctionnent sur la base d’un cas d’utilisation sélectionné. Ces cas d’utilisation sont listés lors de la création d’un garde :
|
|
Dans notre cas d’utilisation, lorsqu’une route est activée, nous devons exécuter un code pour vérifier si l’utilisateur actuel peut naviguer sur la page.
|
|
Pour utiliser la garde, nous devons mettre à jour les itinéraires :
|
|
Intégration de l’API REST
Maintenant que nous avons implémenté l’API d’authentification, l’implémentation d’une API de données ne sera pas difficile.
Je vais simplement partager une meilleure pratique concernant la communication entre l’application Angular et l’API que vous consommez.
Mettre à jour le service existant avec des appels d’API
Dans notre exemple d’application, l’API renvoie des cartes, donc tout d’abord, nous allons devoir créer un fichier interfaces/card.interface.ts pour définir le contrat entre le Frontend et le Backend :
|
|
Ensuite, nous modifions le fichier card.model.ts pour que :
-
il implémente l’interface.
1export class Card implements ICard {} -
il définit une méthode statique pour convertir une carte au format JSON en une instance de
Carte.1 2 3static fromJson(cardJson: ICard): Card { return Object.assign(new Card(), cardJson); } -
il définit une méthode pour convertir une instance de
Carden une carte au format JSON.1 2 3 4 5 6 7toJson(): ICard { const cardJson: ICard = Object.assign({}, this); // L'`id` doit être supprimé car il est soit nécessaire (Create) // soit présent dans l'URI du point final (Update). delete cardJson.id; return cardJson; }
Ensuite, nous pouvons mettre à jour CardService pour interroger l’API REST :
|
|
Mettre à jour les composants de la liste
Dans les composants de la liste de cartes, nous avons ceci :
|
|
Mais ESLint nous dit :
|
|
Nous pourrions utiliser un subscribe sur le résultat de getAll pour convertir le Observable<Card[]> en Card[], mais Angular fournit en fait une méthode plus simple appelée toSignal :
|
|
Par conséquent, vous pouvez supprimer le code du constructeur.
Mettre à jour le composant de la carte unique
Dans ce cas, l’adaptation du code nécessite une approche différente, mais encore une fois, pour éviter les subscribe imbriqués, Sergio montre l’utilisation de switchMap dans un pipe :
|
|
De cette façon, nous n’avons besoin de désabonner qu’un seul abonnement.
Pour la méthode submit, nous devons adapter le code :
|
|
En ce qui concerne deleteCard, nous devons également adapter le code :
|
|
Conclusion
Personnellement, je préfère la syntaxe de Vue. Mais par rapport à React, l’utilisation d’Angular me semble plus structurée.
En ce qui concerne le cours sur YouTube, je pense que Sergio a fait un excellent travail et j’ai appris tout ce dont j’avais besoin pour vraiment comprendre les bases d’Angular.
De plus, ayant une expérience avec Vue 3, j’ai compris les concepts de signal plus rapidement, car je pouvais relier la syntaxe et l’API équivalentes avec Vue.
J’ai besoin de pratiquer régulièrement maintenant, surtout en ce qui concerne les pipe, subscribe, tap, map. Il enseigne la partie RxJs dans une autre vidéo que je suivrai bientôt.
RxJs plus en détails
À la fin du cours, Sergio partage une astuce sur les bonnes pratiques lorsque vous gérez plusieurs abonnements dans un seul composant.
En fait, au lieu d’utiliser un abonnement par cas d’utilisation, nous pouvons déclarer une seule variable de type Subscription pour tous :
|
|
Ensuite, dans chaque cas d’utilisation, nous effectuons ce qui suit :
|
|
Et nous mettons à jour le corps ngOnDestroy avec ceci :
|
|
Pour plus d’informations sur le sujet, je recommande les vlogs de Sergio sur le sujet.
- Intro à RxJS - Observables, Observers, Subscriptions
- RxJS / Angular : Opérateurs et exemples concrets
Suivez-moi !
Merci d’avoir lu cet article. Assurez-vous de me suivre sur X, de vous abonner à ma publication Substack et d’ajouter mon blog à vos favoris pour ne pas manquer les prochains articles.
Photo de RealToughCandy.com.