Offre estivale Bibliothèque Numérique ENI :
50€ offerts pour préparer la rentrée ! Cliquez ici
Formez-vous en autonomie à Excel, Word, VBA, Microsoft 365…. Cliquez ici
  1. Livres & vidéos
  2. Angular
  3. Les services
Extrait - Angular Développez vos applications web avec le framework JavaScript de Google (4e édition)
Extraits du livre
Angular Développez vos applications web avec le framework JavaScript de Google (4e édition)
1 avis
Revenir à la page d'achat du livre

Les services

Introduction

Dans une application Angular, les services jouent un rôle central en facilitant la séparation des responsabilités et en favorisant la réutilisation du code. Comme la plupart des éléments Angular, ce sont des classes TypeScript, mais alors que les composants se concentrent sur la gestion de l’interface utilisateur et les interactions avec celle-ci, les services sont responsables de la logique métier, des opérations asynchrones et des interactions avec des sources de données externes, comme des API. Ils sont le cœur de la communication et de la gestion des données, permettant de centraliser des fonctionnalités partagées telles que l’accès aux données, la gestion d’état ou, de manière plus précise, l’authentification.

Ce chapitre explore en détail les services Angular, c’est-à-dire que nous ne verrons pas seulement à quoi ressemble un service, mais surtout comment traiter les données au sein des services. D’abord, par la création d’un simple service et comment y gérer une donnée basique. Dans un second temps, nous verrons l’asynchronisme avec les Promises puis RxJS, qui est un élément essentiel de l’écosystème Angular. Enfin, nous verrons des services natifs proposés par Angular comme le service...

Création et utilisation simple d’un service

1. Création

Commençons par créer notre premier service :

ng g service services/first 

Cela va créer notre nouveau fichier au chemin suivant :

src/app/services/first.service.ts.

import { Injectable } from '@angular/core'; 
 
@Injectable({ 
  providedin: 'root' 
}) 
export class FirstService { 
 
  constructor() { } 
} 

Seul le strict minimum est généré et la première chose qui se remarque est le décorateur ; il s’appelle Injectable, car un service n’est qu’une classe gérée par le mécanisme de gestion de dépendances. Pour faire court, l’injection de dépendances est un système qui décide de créer une nouvelle instance de classe lorsque cela est nécessaire et nous fournit la bonne instance lorsque nous en avons besoin. Les éléments gérés par ce mécanisme ne doivent pas être instanciés manuellement, mais nous verrons ceci en détail dans la section L’injection de dépendances.

Pour ce qui est des paramètres du décorateur, ils sont tous liés à l’injection de dépendances. Un seul est fourni pour le moment : providedIn. Cette option n’est pas obligatoire, mais elle permet de définir l’étendue d’une même instance au sein d’Angular. Ici, la valeur root indique qu’une même instance du service sera accessible partout dans l’application courante et donc que les ressources seront partagées pour tous. Pour le moment, nous allons laisser cette option telle quelle afin de nous concentrer sur l’utilisation d’un service.

2. Utiliser notre service

Dans un premier temps, nous cherchons à comprendre le rôle d’un service. Nous avons vu qu’ils sont fortement liés à la gestion des données, donc utilisons un service afin de récolter des données et de la partager à travers des composants.

Pour accéder à un service depuis un composant, il y a deux méthodes : soit en ajoutant un paramètre dans le constructeur de votre composant, soit en utilisant la fonction...

L’asynchronisme

1. Introduction et rappel

Le langage JavaScript est limité à un seul fil d’exécution (ou thread), ce qui signifie que l’on ne peut exécuter qu’une seule tâche à la fois. Étant donné que le navigateur a besoin d’exécuter un bon nombre de tâches pour rafraîchir la page si nous devions lancer une tâche longue, celle-ci empêcherait toute autre opération et l’utilisateur verrait son interface gelée, dans l’incapacité d’effectuer le moindre mouvement. L’asynchronisme désigne la capacité du langage à gérer plusieurs tâches simultanément (ou presque) sans bloquer l’exécution du reste du programme.

2. Les Promises

a. Introduction

Les promesses (ou Promises) sont un concept clé en JavaScript pour gérer l’asynchronisme. Elles jouent un rôle important dans les applications web, notamment pour les opérations longues comme les appels réseau ou les lectures de fichiers.

Une Promise est un objet qui représente l’état, l’achèvement d’une tâche asynchrone, contenant un résultat. L’utiliser permet d’exécuter du code de manière non bloquante, ce qui signifie que l’application peut continuer de fonctionner pendant que l’opération asynchrone se poursuit.

Nous pouvons observer les Promises sous trois états différents :

  • Pending (en attente) : l’opération est en cours d’exécution et n’a pas encore produit de résultat.

  • Fulfilled (résolue) : l’opération a réussi et un résultat est disponible.

  • Rejected (rejetée) : l’opération a échoué et le résultat représente une erreur.

Pour créer une Promise, nous devons instancier l’objet avec la classe du même nom, avec en paramètre une fonction de rappel (callback) que l’on appelle resolver. Celui-ci recevra en paramètre deux autres fonctions : l’une à utiliser en cas de succès et l’autre en cas d’échec. Voilà un exemple :

const myAsyncTask = new Promise((resolve,reject)=>{ 
 
  try { 
    let...

Signaux ou Observables ?

1. Passer de l’un à l’autre

Dans le cas des services, les Observables sont fortement utilisés, que cela soit pour gérer une requête de données au serveur ou partager une donnée à travers diverses sections de notre application. Dans l’exemple qui suit, nous disposons d’un sujet privé, exposé publiquement via un Observable.

@Injectable({ 
  providedIn: 'root', 
}) 
export class FirstService { 
  private _searchResult: BehaviorSubject<string[]> = new 
BehaviorSubject<string[]>([]); 
  searchResult$: Observable<string[]> = 
this._searchResult.asObservable(); 
 
  searchResult = useObservable(searchResult$); 
  constructor() {} 
  sendNotification(e:string) {} 
} 

Maintenant que nous voulons utiliser les signaux depuis nos composants, au lieu d’exposer un Observable et d’avoir à y souscrire, nous pouvons créer un signal à partir de notre BehaviorSubject. Pour ce faire, nous pouvons remplacer la déclaration de searchResult$ par ce qui suit :

import { toSignal } from '@angular/core/rxjs-interop'; 
... 
searchResult = toSignal(_searchResult); 

Nous disposons de deux méthodes : toSignal et toObservable...

Les requêtes HTTP

1. Introduction

Lors de communications avec des API HTTP, on utilise généralement les méthodes natives de JavaScript telles que fetch, qui est moderne et flexible. Cependant, Angular nous propose un service nommé HttpClient, une abstraction un peu plus haut niveau qui va offrir une interface simplifiée pour les communications HTTP fournissant aussi quelques fonctionnalités non négligeables, comme le fait d’intégrer un système d’interception de requêtes, ou de faciliter la manipulation des réponses grâce aux Observables.

2. Créer le substitut d’une API

Lorsque l’on développe une application front-end qui consomme une API, il arrive que l’implémentation des fonctionnalités de l’API ne soit pas encore accessible. 

Dans ces situations, il est nécessaire de simuler les réponses de l’API pour tester et développer l’application. Une option serait de créer une véritable API temporaire, mais cela peut être coûteux en temps et en efforts. C’est ici qu’interviennent des solutions comme Postman ou MirageJS. Postman est un outil permettant de simuler des appels d’API manuellement. Habituellement utilisé pour tester les API, il intègre cependant une fonctionnalité de serveur local. Tandis que MirageJS offre une solution intégrée dans le code front-end, permettant de définir des routes et des réponses d’API simulées, ce qui simplifie grandement le développement et le test en local.

a. Postman

Postman, dont le site officiel est https://www.postman.com/, est un outil largement utilisé dans le développement web pour tester et automatiser des requêtes HTTP. Il permet aux développeurs d’interagir facilement avec des API, d’exécuter des tests et de visualiser les réponses en temps réel. En plus de ces fonctionnalités de base, Postman offre également la possibilité de créer des substituts (mocks) d’API. Cette fonctionnalité est particulièrement utile pour simuler des points de terminaison (endpoints) et tester des intégrations avant même que l’API réelle ne soit complètement développée, facilitant ainsi la collaboration...

D’autres services existants

Tout comme pour le client HTTP, Angular fournit un ensemble de services natifs qui facilitent le développement d’applications en offrant des fonctionnalités communes prêtes à l’emploi. Ces services permettent aux développeurs d’éviter de réinventer la roue et de bénéficier de solutions éprouvées pour diverses tâches, telles que la gestion de la navigation, l’interaction avec le DOM et l’affichage de notifications. Ils sont intégrés au framework et suivent les principes d’injection de dépendances, garantissant une utilisation cohérente et modulable.

Connaître ces services existants permet non seulement de simplifier le développement, mais aussi de maintenir un code plus propre et plus standardisé.

1. Router

Le service Router permet de gérer la navigation et les transitions de pages dans une application Angular. Il est utilisé pour définir les routes, naviguer entre les vues et interagir avec l’historique de navigation du navigateur. Avec Router, il est possible de récupérer les informations sur la route active, rediriger les utilisateurs et protéger certaines routes avec des gardes d’authentification. Le chapitre Naviguer entre les pages est dédié aux concepts avancés liés au Router, tels...

L’injection de dépendances

L’injection de dépendances (DI) est un concept central dans Angular qui permet d’injecter des services ou des objets dans les classes où ils sont nécessaires. Au lieu de créer manuellement les instances de services, Angular fournit ces services via un système d’injection, ce qui favorise la modularité, la réutilisabilité et la testabilité du code, par exemple en permettant de choisir si une instance d’un service doit être partagée entre plusieurs composants ou recréée pour chaque utilisation. Cette flexibilité est utile pour gérer des contextes partagés ou les isoler selon les besoins.

L’avantage principal de la DI est de découpler les composants de leurs dépendances, permettant ainsi de les remplacer facilement (par exemple, lors de tests). En structurant le code de cette façon, Angular assure une meilleure maintenabilité et facilite l’évolution des applications.

1. Fonctionnement

Angular utilise un système d’injecteurs hiérarchiques pour fournir des services. Ces injecteurs sont organisés en niveaux :

  • L’injecteur de plateforme, qui est le parent de tous les autres, correspond au plus vaste contexte que l’on peut avoir. Il peut comprendre plusieurs applications Angular si votre architecture est faite pour contenir plusieurs "app" au sein d’une même page.

  • L’injecteur racine (root) fournit les services globalement dans toute l’application.

  • Les injecteurs locaux (modules, composants) peuvent fournir des instances distinctes pour des contextes spécifiques. Cela permet de contrôler si un service est partagé ou isolé.

a. Enregistrer un provider

Il existe plusieurs moyens de définir une configuration d’injection. D’abord, la configuration globale se trouve dans le fichier par défaut app.config.ts ; si ce fichier n’existe pas dans votre projet, la configuration est le second paramètre passé à la méthode bootstrapApplication. Cette configuration contient une propriété providers pour y définir les règles d’injections au niveau global. Ensuite, chaque module et chaque composant peut aussi fournir une propriété providers dans leur...

Projet fil rouge

Cette étape de progression dans le projet se déroule en trois phases. Le premier objectif est de réorganiser notre code, non seulement pour nous rapprocher de la réalité en utilisant une (fausse) API, mais aussi pour réajuster les responsabilités de chaque élément de notre application. Puis nous ajouterons une notion de connexion d’un utilisateur, pour le moment via un simple bouton, mais qui actionnera un mécanisme d’authentification. Enfin, une fois la connexion possible, nous ferons évoluer notre interface et certains composants afin de nous adapter à la notion d’utilisateur en cachant le tableau tant que l’utilisateur n’est pas connecté, par exemple.

1. Configuration de l’API

Pour commencer, nous avons besoin d’une API fonctionnelle. Le choix de la technologie importe peu pour cet exercice : vous êtes libre d’utiliser l’outil qui vous convient le mieux. Ce qui est essentiel, c’est de mettre en place au minimum les routes suivantes :

  • récupération globale des tickets : via une requête HTTP GET sur "/api/board", retournant un objet { board:  Board } ;

  • modification de l’ordre d’un ticket :

  • requête HTTP PATCH sur "/api/board/ticket/reorder/:ticketId",

  • body de type {to: {ticketId: string, columnId: string}},

  • réponse : un objet { board:  Board }.

  • création d’un nouveau ticket :

  • requête HTTP POST sur "/api/board/ticket/:columnId",

  • body de type {ticket: Ticket},

  • réponse : un objet { createdTicket: Ticket.

  • connexion :

  • requête HTTP POST sur "/api/login",

  • body de type {email:string, password:string},

  • réponse : un objet {user:{username:string, isAdmin:boolean}, authToken:string},

  • déconnexion : via une requête HTTP POST sur "/api/logout".

Vous trouverez une suggestion de configuration pour MirageJS dans le repository GitHub du projet.

2. Implémenter les actions relatives aux tickets

a. Définition du service

Nous allons maintenant intégrer cette API dans notre application et cela se fera en plusieurs étapes.

 Ajoutez deux modèles tels que :

export type GetBoardResponse...

Conclusion

Dans ce chapitre, nous avons exploré en détail le rôle central des services dans Angular en les utilisant pour structurer, centraliser et optimiser la logique métier. Nous avons commencé par les bases en apprenant à créer et consommer des services, avant de plonger dans des concepts avancés comme l’asynchronisme avec les Promises et RxJS, la gestion des requêtes HTTP à l’aide du service HttpClient et l’utilisation d’intercepteurs, et enfin des services natifs utiles, comme Router, Title et ErrorHandler, ainsi que les concepts avancés d’injection de dépendances.

Dans la section pratique, nous avons consolidé ces apprentissages en déplaçant la logique métier hors des composants pour la centraliser dans des services, supprimant ainsi le code en dur et améliorant la modularité de l’application Kanban. La mise en place d’une API locale et l’intégration d’une authentification simplifiée ont permis de synchroniser les données et les actions en fonction de l’état utilisateur, tout en posant les bases d’une application extensible et maintenable.

Le code final est disponible sur GitHub à l’URL suivante : https://github.com/KevinAlbrecht/angular-eni/tree/chapitre-7. À partir de maintenant, les possibilités d’étendre...