🔴 Live Angular le 16 octobre à 19h

Anticipez le futur avec Signal Forms d'Angular

Angular 21 introduira Signal Forms, une nouvelle API expérimentale qui simplifiera radicalement la gestion des formulaires.

Basée sur les signaux, elle permettra :

  • de créer des formulaires déclaratifs avec form()
  • de lier directement les champs avec [control]
  • d'intégrer facilement les validations et la soumission
  • et de réduire le boilerplate tout en améliorant les performances
Skip to content

Vous souhaitez recevoir de l'aide sur ce sujet ? rejoignez la communauté Angular.fr sur Discord.

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();
  }
}