Appearance
Injection de dépendances et la fonction inject()
Quel est le but (théorie)
L'injection de dépendances (DI) est un modèle de conception logicielle qui permet de supprimer les dépendances codées en dur dans notre application.
L'injection de dépendances est utilisée pour passer une instance d'une classe à un objet dépendant. Par exemple, si nous avons une variable, par exemple client, dans notre composant, Angular se chargera de fournir un objet client réel au composant.
Angular utilise une classe appelée Injector pour faire du DI. Injector est une instance singleton dont le travail consiste à créer des instances de classes. L'injecteur utilise un ensemble de règles pour répondre à la demande de dépendance.
Savoir utiliser la fonction inject()
L'instruction inject
dans Angular permet d'injecter des dépendances dans des fonctions, ce qui offre une manière alternative d'injecter des dépendances par rapport à la méthode traditionnelle basée sur les classes. Cette fonctionnalité peut s'avérer utile dans diverses situations.
Avantages
Réutilisabilité L'utilisation de
inject
facilite le partage de code entre les composants. C'est un avantage significatif, car cela permet d'écrire des fonctions qui peuvent être utilisées dans plusieurs composants sans avoir à injecter les mêmes dépendances à chaque fois. C'est particulièrement avantageux lorsque de nombreux composants utilisent les mêmes dépendances.Inférence de type Auparavant, lorsqu'on créait une classe utilisant un token d'injection, il fallait définir explicitement le type de la propriété qui contiendrait la valeur injectée. Ce n'est plus nécessaire avec
inject
, car le type de la valeur injectée est déduit du type du token d'injection.Héritage dans Angular avec des dépendances injectées
Étendre les composants, directives et autres dans Angular à partir d'autres classes a toujours été assez délicat, en particulier lorsque nous utilisons des dépendances injectées. Le problème réside dans le fait qu'avec l'injection par le constructeur, nous devons passer les dépendances au constructeur parent. Cela nécessite d'écrire du code répétitif et devient de plus en plus complexe à mesure que la chaîne d'héritage s'allonge.
Exemple :
Prenons deux services
ServiceA
etServiceB
que nous souhaitons injecter dans des classes héritées.typescript@Injectable() export class ServiceA { /* ... */ } @Injectable() export class ServiceB { /* ... */ }
Imaginons maintenant que nous ayons une classe
BaseComponent
qui nécessiteServiceA
, et une classeDerivedComponent
qui hérite deBaseComponent
et nécessite à la foisServiceA
etServiceB
.typescriptexport class BaseComponent { constructor(protected serviceA: ServiceA) { /* ... */ } } export class DerivedComponent extends BaseComponent { constructor(serviceA: ServiceA, private serviceB: ServiceB) { super(serviceA); // Le reste du code... } }
Comme le montre cet exemple, même si
DerivedComponent
hérite deBaseComponent
, nous devons réinjecterServiceA
dansDerivedComponent
et le passer au constructeur parent viasuper(serviceA)
. Cette répétitivité et cette complexité peuvent rapidement augmenter à mesure que nous ajoutons plus de classes dans la chaîne d'héritage ou plus de dépendances injectées.La fonction
inject
peut aider à simplifier ce scénario en permettant l'injection de dépendances directement dans les fonctions et les méthodes sans avoir besoin de passer par les constructeurs, rendant ainsi l'héritage plus maniable dans Angular.
Inconvénient
Disponibilitéinject
est uniquement disponible dans les contextes d'injection de dépendances. Par conséquent, son utilisation dans des classes de modèle ou DTO entraînerait des erreurs.
Exemple: Tester un Guard
Utilisation de la fonction inject()
Voici comment vous pouvez utiliser la fonction inject
pour injecter un UserService
dans un composant Angular :
typescript
import { Component, OnInit } from '@angular/core';
import { User } from "src/app/core/interfaces/user";
import { UserService } from 'src/app/core/services/user.service';
import { inject } from '@angular/core';
@Component({
selector: 'app-users',
standalone: true,
template: `
<div>
Nombre d'utilisateurs : {{ users().length }}
</div>
`
})
export class UserComponent implements OnInit {
private userService = inject(UserService);
users = this.userService.users;
// Utilisez la fonction inject pour obtenir une instance de UserService
ngOnInit() {
this.userService.getAll().subscribe();
}
}
ts
import { HttpClient } from "@angular/common/http";
import { Injectable, inject, signal } from "@angular/core";
import { Observable, tap } from "rxjs";
import { User } from "../interfaces/user";
@Injectable({
providedIn: "root",
})
export class UserService {
private http = inject(HttpClient);
readonly url: string = "https://jsonplaceholder.typicode.com/users";
users = signal<User[]>([]);
getAll(): Observable<User[]> {
return this.http.get<User[]>(this.url).pipe(
tap((users) => {
this.users.set(users);
})
);
}
}
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;
};
}
Notez que nous avons directement injecté le UserService
à l'aide de inject
sans avoir besoin de le faire via le constructeur. Cela peut rendre votre code plus concis et offre une alternative à l'injection classique basée sur le constructeur.