La sortie de Angular v19, il y a quelques semaines seulement, marque une étape importante dans la révolution du signal au sein du framework, avec le Input, Modèle, Sortie et Signal RequêtesLes API sont désormais officiellement promues au statut stable.
Mais ce n'est pas tout ! Cette version majeure introduit également de nouveaux outils puissants conçus pour faire progresser encore la révolution du signal : la nouvelle API de ressources.
Comme son nom l'indique, cette nouvelle API de ressources est conçue pour simplifier le chargement des ressources asynchrones en exploitant toute la puissance des signaux !
IMPORTANT : au moment de la rédaction, la nouvelle API de ressources est encore expérimentale. Cela signifie qu'il peut changer avant de devenir stable, alors utilisez-le à vos propres risques. ?
Plongeons dans son fonctionnement et comment il simplifie la gestion des ressources asynchrones !
La plupart des API de signal sont synchrones, mais dans les applications du monde réel, il est essentiel de gérer des ressources asynchrones, telles que la récupération de données sur un serveur ou la gestion des interactions des utilisateurs en temps réel.
C'est là que la nouvelle API Resource entre en jeu.
En utilisant une Ressource, vous pouvez facilement consommer une ressource asynchrone via des signaux, vous permettant de gérer facilement la récupération de données, de gérer les états de chargement et de déclencher une nouvelle récupération chaque fois que les paramètres de signal associés changent.
Le moyen le plus simple de créer une Ressource consiste à utiliser la fonction resource() :
import { resource, signal } from '@angular/core'; const RESOURCE_URL = 'https://jsonplaceholder.typicode.com/todos/'; private id = signal(1); private myResource = resource({ request: () => ({ id: this.id() }), loader: ({ request }) => fetch(RESOURCE_URL + request.id), });
Cette fonction accepte un objet de configuration ResourceOptions en entrée, vous permettant de spécifier les propriétés suivantes :
Grâce à ces configurations, nous pouvons facilement définir une dépendance asynchrone qui sera toujours consommée efficacement et maintenue à jour.
Une fois qu'une Ressource est créée, la fonction loader est exécutée, puis la requête asynchrone résultante démarre :
import { resource, signal } from '@angular/core'; const RESOURCE_URL = 'https://jsonplaceholder.typicode.com/todos/'; private id = signal(1); private myResource = resource({ request: () => ({ id: this.id() }), loader: ({ request }) => fetch(RESOURCE_URL + request.id), });
Chaque fois qu'un signal indiquant que la fonction request dépend de changements, la fonction request s'exécute à nouveau, et si elle renvoie de nouveaux paramètres, la fonction loader est déclenchée pour récupérer la valeur de la ressource mise à jour :
import { resource, signal } from "@angular/core"; const RESOURCE_URL = "https://jsonplaceholder.typicode.com/todos/"; const id = signal(1); const myResource = resource({ request: () => ({ id: id() }), loader: ({ request }) => fetch(RESOURCE_URL + request.id) }); console.log(myResource.status()); // Prints: 2 (which means "Loading")
Si aucune fonction request n'est fournie, la fonction loader ne s'exécutera qu'une seule fois, à moins que la Resource ne soit rechargée à l'aide du reload méthode (plus ci-dessous).
Enfin, une fois le composant ou service parent détruit, la Ressource est également détruite sauf si un injecteur spécifique a été prévu.
Dans de tels cas, la Ressource restera active et ne sera détruite que lorsque l'injecteur fourni lui-même sera détruit.
Pour optimiser la récupération des données, une Ressource peut abandonner une requête en attente si le calcul request() change alors qu'une valeur précédente est toujours en cours de chargement.
Pour gérer cela, la fonction loader() fournit un abortSignal, que vous pouvez transmettre aux requêtes en cours, telles que fetch. La requête écoute le abortSignal et annule l'opération si elle est déclenchée, garantissant ainsi une gestion efficace des ressources et évitant les requêtes réseau inutiles :
import { resource, signal } from "@angular/core"; const RESOURCE_URL = "https://jsonplaceholder.typicode.com/todos/"; const id = signal(1); const myResource = resource({ request: () => ({ id: id() }), loader: ({ request }) => fetch(RESOURCE_URL + request.id) }); console.log(myResource.status()); // Prints: 2 (which means "Loading") // After the fetch resolves console.log(myResource.status()); // Prints: 4 (which means "Resolved") console.log(myResource.value()); // Prints: { "id": 1 , ... } id.set(2); // Triggers a request, causing the loader function to run again console.log(myResource.status()); // Prints: 2 (which means "Loading") // After the fetch resolves console.log(myResource.status()); // Prints: 4 (which means "Resolved") console.log(myResource.value()); // Prints: { "id": 2 , ... }
Sur cette base, il est recommandé d'utiliser l'API Resource principalement pour les requêtes GET, car elles peuvent généralement être annulées en toute sécurité sans causer de problèmes.
Pour les demandes POST ou UPDATE, l'annulation peut entraîner des effets secondaires involontaires, tels que des soumissions ou des mises à jour de données incomplètes. Cependant, si vous avez besoin de fonctionnalités similaires pour ces types de requêtes, vous pouvez utiliser la méthode effect() pour gérer les opérations en toute sécurité.
L'API Resource fournit plusieurs propriétés de signal pour son état, que vous pouvez facilement utiliser directement au sein de vos composants ou services :
Voici un exemple de la façon de consommer une Ressource au sein d'un composant :
import { resource, signal } from '@angular/core'; const RESOURCE_URL = 'https://jsonplaceholder.typicode.com/todos/'; private id = signal(1); private myResource = resource({ request: () => ({ id: this.id() }), loader: ({ request }) => fetch(RESOURCE_URL + request.id), });
Dans cet exemple, la Resource est utilisée pour récupérer des données d'une API en fonction de la valeur du signal id, qui peut être incrémentée en cliquant sur un bouton.
Chaque fois que l'utilisateur clique sur le bouton, la valeur du signal id change, déclenchant la fonction loader pour récupérer un nouvel élément à partir de l'API distante.
L'interface utilisateur se met automatiquement à jour avec les données récupérées grâce aux propriétés des signaux exposées par l'API Resource.
Comme mentionné précédemment, le signal status fournit des informations sur l'état actuel de la ressource à un moment donné.
Les valeurs possibles du signal status sont définies par l'énumération ResourceStatus. Voici un récapitulatif de ces statuts et de leurs valeurs correspondantes :
Ces statuts permettent de suivre la progression de la Ressource et facilitent une meilleure gestion des opérations asynchrones dans votre application.
Compte tenu de la complexité de ces statuts, l'API Resource fournit une méthode hasValue(), qui renvoie un booléen basé sur le statut actuel.
Cela garantit des informations précises sur l'état de la Ressource, offrant un moyen plus fiable de gérer les opérations asynchrones sans compter sur la valeur, qui peut être non définie dans certains états.
import { resource, signal } from '@angular/core'; const RESOURCE_URL = 'https://jsonplaceholder.typicode.com/todos/'; private id = signal(1); private myResource = resource({ request: () => ({ id: this.id() }), loader: ({ request }) => fetch(RESOURCE_URL + request.id), });
Cette méthode est réactive, vous permettant de la consommer et de la suivre comme un signal.
L'API Resource fournit également un signal isLoading, qui renvoie si la ressource est actuellement dans l'état Loading ou Reloading :
import { resource, signal } from "@angular/core"; const RESOURCE_URL = "https://jsonplaceholder.typicode.com/todos/"; const id = signal(1); const myResource = resource({ request: () => ({ id: id() }), loader: ({ request }) => fetch(RESOURCE_URL + request.id) }); console.log(myResource.status()); // Prints: 2 (which means "Loading")
Puisque isLoading est un signal calculé, il peut être suivi de manière réactive, vous permettant de surveiller l'état de chargement en temps réel à l'aide des API de signaux.
Le signal de valeur fourni par une Resource est un WritableSignal, qui permet de le mettre à jour manuellement à l'aide des set() et update( ) fonctions :
import { resource, signal } from "@angular/core"; const RESOURCE_URL = "https://jsonplaceholder.typicode.com/todos/"; const id = signal(1); const myResource = resource({ request: () => ({ id: id() }), loader: ({ request }) => fetch(RESOURCE_URL + request.id) }); console.log(myResource.status()); // Prints: 2 (which means "Loading") // After the fetch resolves console.log(myResource.status()); // Prints: 4 (which means "Resolved") console.log(myResource.value()); // Prints: { "id": 1 , ... } id.set(2); // Triggers a request, causing the loader function to run again console.log(myResource.status()); // Prints: 2 (which means "Loading") // After the fetch resolves console.log(myResource.status()); // Prints: 4 (which means "Resolved") console.log(myResource.value()); // Prints: { "id": 2 , ... }
Remarque : comme vous pouvez le voir, la mise à jour manuelle de la valeur du signal définira également l'état sur 5, ce qui signifie "Local ", pour indiquer que la valeur a été définie localement.
La valeur définie manuellement persistera jusqu'à ce qu'une nouvelle valeur soit définie ou qu'une nouvelle requête soit effectuée, qui la remplacera par une nouvelle valeur :
import { resource, signal } from "@angular/core"; const RESOURCE_URL = "https://jsonplaceholder.typicode.com/todos/"; const id = signal(1); const myResource = resource({ request: () => ({ id: id() }), loader: ({ request, abortSignal }) => fetch(RESOURCE_URL + request.id, { signal: abortSignal }) }); console.log(myResource.status()); // Prints: 2 (which means "Loading") // Triggers a new request, causing the previous fetch to be aborted // Then the loader function to run again generating a new fetch request id.set(2); console.log(myResource.status()); // Prints: 2 (which means "Loading")
Remarque : le signal value de l'Resource API utilise le même modèle de la nouvelle LinkedSignal API, mais n'utilise pas sous le capot. ?
Pour simplifier l'utilisation du signal value, l'API Resource fournit des wrappers pratiques pour le set, la update et asReadonly méthodes.
La méthode asReadonly est particulièrement utile car elle renvoie une instance en lecture seule du signal value, permettant l'accès uniquement en lecture et empêchant toute modification accidentelle.
Vous pouvez utiliser cette approche pour créer des services qui gèrent et suivent les modifications apportées aux valeurs des ressources en exportant une instance en lecture seule de la valeur :
import { Component, resource, signal } from '@angular/core'; const BASE_URL = 'https://jsonplaceholder.typicode.com/todos/'; @Component({ selector: 'my-component', template: ` @if (myResource.value()) { {{ myResource.value().title }} } <button (click)="fetchNext()">Fetch next item</button> ` }) export class MyComponent { private id = signal(1); protected myResource = resource({ request: () => ({ id: this.id() }), loader: ({ request }) => fetch(BASE_URL + request.id).then((response) => response.json()), }); protected fetchNext(): void { this.id.update((id) => id + 1); } }
Cela empêchera les consommateurs de modifier la valeur, réduisant ainsi le risque de changements involontaires et améliorant la cohérence dans la gestion des données complexes.
Lorsque vous travaillez avec des ressources asynchrones, vous pouvez être confronté à des scénarios dans lesquels l'actualisation des données ou la destruction de la Ressource devient nécessaire.
Pour gérer ces scénarios, l'API Resource propose deux méthodes dédiées qui offrent des solutions efficaces pour gérer ces actions.
La méthode reload() demande à la Ressource de réexécuter la requête asynchrone, en s'assurant qu'elle récupère les données les plus à jour :
import { resource, signal } from '@angular/core'; const RESOURCE_URL = 'https://jsonplaceholder.typicode.com/todos/'; private id = signal(1); private myResource = resource({ request: () => ({ id: this.id() }), loader: ({ request }) => fetch(RESOURCE_URL + request.id), });
La méthode reload() renvoie true si un rechargement est lancé avec succès.
Si un rechargement ne peut pas être effectué, soit parce qu'il est inutile, comme lorsque le statut est déjà Chargement ou Rechargement, soit non pris en charge, comme lorsque le statut est Idle, la méthode renvoie false.
La méthode destroy() détruit manuellement la Ressource, détruisant tout effet() utilisé pour suivre les modifications de la demande, annulant toutes les demandes en attente et définissant le statut sur Idle tout en réinitialisant la valeur à non défini :
import { resource, signal } from "@angular/core"; const RESOURCE_URL = "https://jsonplaceholder.typicode.com/todos/"; const id = signal(1); const myResource = resource({ request: () => ({ id: id() }), loader: ({ request }) => fetch(RESOURCE_URL + request.id) }); console.log(myResource.status()); // Prints: 2 (which means "Loading")
Après la destruction d'une Ressource, elle ne répondra plus aux demandes de modifications ou aux opérations reload().
Remarque : à ce stade, tant que le signal valeur reste inscriptible, la Ressource perdra son objectif et ne remplira plus sa fonction, devenant inutile . ?
Comme presque toutes les API basées sur les signaux introduites jusqu'à présent, l'API Resource offre également un utilitaire d'interopérabilité pour une intégration transparente avec RxJS.
Au lieu d'utiliser la méthode resource() pour créer une Resource basée sur la promesse, vous pouvez utiliser la méthode rxResource() pour utiliser Observables :
import { resource, signal } from "@angular/core"; const RESOURCE_URL = "https://jsonplaceholder.typicode.com/todos/"; const id = signal(1); const myResource = resource({ request: () => ({ id: id() }), loader: ({ request }) => fetch(RESOURCE_URL + request.id) }); console.log(myResource.status()); // Prints: 2 (which means "Loading") // After the fetch resolves console.log(myResource.status()); // Prints: 4 (which means "Resolved") console.log(myResource.value()); // Prints: { "id": 1 , ... } id.set(2); // Triggers a request, causing the loader function to run again console.log(myResource.status()); // Prints: 2 (which means "Loading") // After the fetch resolves console.log(myResource.status()); // Prints: 4 (which means "Resolved") console.log(myResource.value()); // Prints: { "id": 2 , ... }
Remarque : la méthode rxResource() est en fait exposée par le package rxjs-interop.
Le Observable produit par la fonction loader() ne considérera que la première valeur émise, ignorant les émissions suivantes.
Merci à tous de m'avoir suivi tout au long de cette merveilleuse année 2024. ??
Cela a été une année pleine de défis, mais aussi très enrichissante. J'ai de grands projets pour 2025 et j'ai hâte de commencer à y travailler. ?
J'aimerais avoir vos commentaires, alors n'hésitez pas à laisser un commentaire, un j'aime ou un suivre. ?
Ensuite, si vous l'avez vraiment aimé, partagez-le avec votre communauté, vos techniciens et qui vous voulez. Et n'oubliez pas de me suivre sur LinkedIn. ??
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!