Appearance
Comment utiliser useExisting dans Angular pour réutiliser des services ?
Le provider useExisting dans Angular permet de réutiliser une instance existante d'un service au lieu d'en créer une nouvelle. C'est particulièrement utile pour créer des alias de services ou étendre des fonctionnalités tout en conservant une instance unique.
Comprendre useExisting avec un exemple concret
Imaginons que vous développez une application de messagerie instantanée comme WhatsApp. Vous avez :
- Un service de base qui gère les messages
- Un service avancé qui ajoute des fonctionnalités (horodatage, statut de lecture, etc.)
Au lieu de créer deux instances séparées qui géreraient les messages indépendamment (ce qui causerait des problèmes de synchronisation), vous voulez que le service avancé utilise la même instance que le service de base.
C'est exactement ce que permet useExisting
: utiliser la même instance d'un service à travers différentes interfaces.
Différences avec les autres providers
Provider | Description | Exemple | Cas d'utilisation | Documentation |
---|---|---|---|---|
useClass | Remplace complètement un service par une autre classe | { provide: UserService, useClass: MockUserService } | Quand vous voulez une implémentation complètement différente (ex: mock pour les tests) | En savoir plus |
useValue | Fournit une valeur fixe | { provide: API_URL, useValue: 'https://api.example.com' } | Pour des constantes ou des objets simples prédéfinis | En savoir plus |
useFactory | Crée dynamiquement une valeur via une fonction | { provide: UserService, useFactory: () => environment.production ? new RealUserService() : new MockUserService() } | Quand la valeur dépend de conditions ou nécessite une logique de création | En savoir plus |
useExisting | Crée un alias vers un service existant | { provide: AbstractLogger, useExisting: ConsoleLogger } | Quand vous voulez utiliser un service existant sous un autre nom | En savoir plus |
Mise en pratique avec notre gestionnaire d'utilisateurs
Voici un exemple concret avec notre système de gestion d'utilisateurs :
ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { User } from './user';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class BaseUserService {
private http = inject(HttpClient);
getUsers(): Observable<User[]> {
return this.http.get<User[]>('/api/users');
}
}
ts
import { Injectable } from '@angular/core';
import { BaseUserService } from './base-user.service';
@Injectable({
providedIn: 'root',
useExisting: BaseUserService
})
export class EnhancedUserService extends BaseUserService {
getUsersWithTimestamp(): Observable<User[]> {
return this.getUsers().pipe(
map(users => users.map(user => ({
...user,
lastAccessed: new Date()
})))
);
}
}
ts
export interface User {
id: number;
name: string;
username?: string;
email: string;
address?: {
street: string;
suite: string;
city: string;
zipcode: string;
geo: {
lat: string;
lng: string;
}
};
phone?: string;
website?: string;
company?: {
name: string;
catchPhrase: string;
bs: string;
};
}
CONSEIL
useExisting est particulièrement utile quand vous voulez ajouter des fonctionnalités à un service existant tout en gardant une seule source de vérité pour les données.
Voici comment l'utiliser dans un composant :
ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
standalone: true,
selector: 'app-user-list',
imports: [CommonModule],
template: `
@for (user of users$ | async; track user.id) {
<div>{{ user.name }} - Dernier accès: {{ user.lastAccessed | date }}</div>
}
`
})
export class UserListComponent {
private enhancedUserService = inject(EnhancedUserService);
users$ = this.enhancedUserService.getUsersWithTimestamp();
}
IMPORTANT
Quand vous utilisez useExisting, assurez-vous que le service de base est bien fourni quelque part dans votre application, sinon Angular ne saura pas quelle instance réutiliser.
Cas d'utilisation typiques
Alias de service : Créer une interface alternative pour un service existant
- Comme avoir différentes vues du même service selon le contexte
Extension de fonctionnalités : Ajouter des fonctionnalités à un service sans le modifier
- Comme ajouter des logs ou de la validation
Migration progressive : Faciliter la transition vers une nouvelle API
- Comme maintenir la compatibilité avec l'ancien code tout en introduisant de nouvelles fonctionnalités
Bonnes pratiques
- Héritage : Toujours étendre la classe du service de base pour garantir la compatibilité des types
- Documentation : Bien documenter la relation entre les services pour faciliter la maintenance
- Singleton : S'assurer que le service de base est bien configuré comme singleton (
providedIn: 'root'
)
ASTUCE
Pour déboguer, vous pouvez vérifier que c'est bien la même instance qui est utilisée en ajoutant un identifiant unique :
ts
constructor() {
console.log('Service ID:', Math.random());
}