Appearance
Le contexte d'injection
Le contexte d'injection est un concept fondamental d'Angular qui détermine quand vous pouvez utiliser la fonction inject()
pour résoudre des dépendances. Sans ce contexte, Angular ne peut pas savoir quel injecteur utiliser, et vous obtiendrez l'erreur NG0203
.
Qu'est-ce que le contexte d'injection ?
Le contexte d'injection est un mécanisme de sécurité et d'organisation qui garantit qu'Angular peut savoir quel injector utiliser pour résoudre vos dépendances. Sans ce contexte, Angular ne saurait pas d'où venir les services que vous demandez.
Pourquoi c'est important ?
Imaginez que vous êtes dans une bibliothèque et que vous voulez emprunter un livre. Vous devez d'abord vous présenter au bibliothécaire (l'injector) qui vérifie votre carte de membre et vous donne accès aux livres. En Angular, le contexte d'injection fonctionne de la même manière : il faut être "présenté" à l'injector pour pouvoir récupérer des services.
Le contexte d'injection est crucial car il :
- Garantit la sécurité : Seuls les endroits autorisés peuvent accéder aux services
- Assure la cohérence : Chaque injection utilise le bon injector (composant, module, root)
- Évite les fuites mémoire : Les services sont correctement liés à leur cycle de vie
- Permet la hiérarchie : Les injectors peuvent être imbriqués (composant → module → root)
Que se passerait-il sans contexte d'injection ?
Sans ce système, vous pourriez avoir des problèmes graves :
typescript
// ❌ Sans contexte d'injection, ceci serait possible partout
function nimporteQuelleFonction() {
// Qui décide quel injector utiliser ?
const service = inject(SomeService); // Erreur ! Quel injector ?
// Problèmes potentiels :
// - Fuites mémoire (services non détruits)
// - Services du mauvais scope
// - Incohérence dans l'application
}
Conséquences sans contexte d'injection :
- 🔴 Fuites mémoire : Services créés mais jamais détruits
- 🔴 Incohérence : Mélange de services de différents scopes
- 🔴 Sécurité compromise : Accès non autorisé aux services
- 🔴 Debugging impossible : Impossible de tracer l'origine des dépendances
Où le contexte est-il disponible ?
Le contexte d'injection est disponible dans ces situations spécifiques :
- Pendant la construction d'une classe instanciée par le système DI
- Dans les initialiseurs de champs de ces classes
- Dans les fonctions factory des providers
- Dans une pile d'exécution lancée via
runInInjectionContext()
Les endroits où vous êtes dans le contexte
1. Dans le constructeur d'un composant ou service
typescript
import { Component, inject } from '@angular/core';
import { UserService } from './user.service';
@Component({
selector: 'app-user-list',
template: `
@for (user of users; track user.id) {
<div>{{ user.name }}</div>
}
`
})
export class UserListComponent {
// ✅ Dans le contexte d'injection
private userService = inject(UserService);
users = this.userService.getUsers();
}
2. Dans les initialiseurs de champs
typescript
import { Component, inject } from '@angular/core';
import { UserService } from './user.service';
@Component({
selector: 'app-user-profile',
template: `
@if (user) {
<h1>{{ user.name }}</h1>
<p>{{ user.email }}</p>
}
`
})
export class UserProfileComponent {
// ✅ Dans le contexte d'injection
private userService = inject(UserService);
// ✅ Aussi dans le contexte
user = this.userService.getCurrentUser();
}
3. Dans les fonctions factory
typescript
import { inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
// ✅ Dans le contexte d'injection
export const API_URL = new InjectionToken<string>('API_URL', {
providedIn: 'root',
factory: () => {
const http = inject(HttpClient);
return 'https://api.example.com';
}
});
Créer un contexte d'injection à la demande
Parfois, vous avez besoin d'utiliser inject()
dans des endroits où le contexte n'est pas automatiquement disponible. C'est là qu'intervient runInInjectionContext()
.
typescript
import { Injectable, inject, runInInjectionContext, EnvironmentInjector } from '@angular/core';
import { UserService } from './user.service';
@Injectable({
providedIn: 'root'
})
export class DataProcessorService {
// Injection de l'EnvironmentInjector pour créer des contextes
private environmentInjector = inject(EnvironmentInjector);
processData() {
// ❌ Hors contexte d'injection
// const userService = inject(UserService); // Erreur NG0203 !
// ✅ Créer un contexte d'injection
runInInjectionContext(this.environmentInjector, () => {
const userService = inject(UserService);
const users = userService.getUsers();
// Traitement des données...
});
}
}
Les erreurs courantes et leurs solutions
Erreur : Appeler inject() dans un hook de cycle de vie
typescript
@Component({
selector: 'app-bad-example',
template: `<div>{{ message }}</div>`
})
export class BadExampleComponent {
message = '';
ngOnInit() {
// ❌ Trop tard ! Le contexte d'injection n'est plus disponible
// const userService = inject(UserService); // Erreur NG0203 !
}
}
Solution : Déplacer l'injection dans un initialiseur de champ ou le constructeur
typescript
@Component({
selector: 'app-good-example',
template: `<div>{{ message }}</div>`
})
export class GoodExampleComponent {
// ✅ Dans le contexte d'injection
private userService = inject(UserService);
message = this.userService.getWelcomeMessage();
}
Erreur : Appeler inject() après un await
typescript
async loadData() {
const response = await fetch('/api/data');
// ❌ Après un await, on n'est plus dans le contexte d'injection
// const userService = inject(UserService); // Erreur NG0203 !
}
Solution : Injecter d'abord, puis faire l'opération asynchrone
typescript
async loadData() {
// ✅ Injecter avant l'opération asynchrone
const userService = inject(UserService);
const response = await fetch('/api/data');
const data = await response.json();
// Utiliser userService ici
userService.processData(data);
}
Vérifier le contexte d'injection
Angular fournit assertInInjectionContext()
pour vérifier qu'on est bien dans un contexte d'injection :
typescript
import { assertInInjectionContext, inject } from '@angular/core';
import { UserService } from './user.service';
function myHelperFunction() {
// Vérifier qu'on est dans le contexte
assertInInjectionContext(myHelperFunction);
// Maintenant on peut utiliser inject() en toute sécurité
const userService = inject(UserService);
return userService.getUsers();
}
Bonnes pratiques
Conseil
Préférez les initialiseurs de champs pour inject()
car ils sont plus lisibles et sûrs temporellement.
typescript
@Component({
selector: 'app-best-practice',
template: `<div>{{ user?.name }}</div>`
})
export class BestPracticeComponent {
// ✅ Bonne pratique : injection dans l'initialiseur de champ
private userService = inject(UserService);
// ✅ Aussi dans l'initialiseur
user = this.userService.getCurrentUser();
}
Attention
Gardez inject()
synchrone. Stockez les dépendances avant de lancer des flux asynchrones.
typescript
@Component({
selector: 'app-async-example',
template: `<div>{{ data }}</div>`
})
export class AsyncExampleComponent implements OnInit {
// ✅ Stocker la dépendance avant l'async
private dataService = inject(DataService);
data = '';
async ngOnInit() {
// ✅ Utiliser la dépendance stockée
this.data = await this.dataService.fetchData();
}
}