Skip to content

Savoir utiliser la fonction inject() depuis Angular 14+

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 et inconvénient de la fonction inject()

Avantages

  1. 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.

  2. 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.

  3. 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 et ServiceB que nous souhaitons injecter dans des classes héritées.

    typescript
    @Injectable()
    export class ServiceA { /* ... */ }
    
    @Injectable()
    export class ServiceB { /* ... */ }
    @Injectable()
    export class ServiceA { /* ... */ }
    
    @Injectable()
    export class ServiceB { /* ... */ }

    Imaginons maintenant que nous ayons une classe BaseComponent qui nécessite ServiceA, et une classe DerivedComponent qui hérite de BaseComponent et nécessite à la fois ServiceA et ServiceB.

    typescript
    export class BaseComponent {
        constructor(protected serviceA: ServiceA) { /* ... */ }
    }
    
    export class DerivedComponent extends BaseComponent {
        constructor(serviceA: ServiceA, private serviceB: ServiceB) {
            super(serviceA);
            // Le reste du code...
        }
    }
    export 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 de BaseComponent, nous devons réinjecter ServiceA dans DerivedComponent et le passer au constructeur parent via super(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',
  template: `
    <div>
      Nombre d'utilisateurs : {{ users.length }}
    </div>
  `
})
export class UserComponent implements OnInit {
  users: User[] = [];

  // Utilisez la fonction inject pour obtenir une instance de UserService
  private userService: UserService = inject(UserService);

  ngOnInit() {
    this.userService.getAll().subscribe(users => this.users = users);
  }
}
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',
  template: `
    <div>
      Nombre d'utilisateurs : {{ users.length }}
    </div>
  `
})
export class UserComponent implements OnInit {
  users: User[] = [];

  // Utilisez la fonction inject pour obtenir une instance de UserService
  private userService: UserService = inject(UserService);

  ngOnInit() {
    this.userService.getAll().subscribe(users => this.users = users);
  }
}
ts
import { HttpClient } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { Observable } from "rxjs";
import { User } from "src/app/core/interfaces/user";

@Injectable({
    providedIn: 'root'
})
export class UserService {
    readonly url: string = 'https://jsonplaceholder.typicode.com/users'

    constructor(
        private http: HttpClient
    ) {}

    getAll(): Observable<User[]> {
        return this.http.get<User[]>(this.url)
    }
}
import { HttpClient } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { Observable } from "rxjs";
import { User } from "src/app/core/interfaces/user";

@Injectable({
    providedIn: 'root'
})
export class UserService {
    readonly url: string = 'https://jsonplaceholder.typicode.com/users'

    constructor(
        private http: HttpClient
    ) {}

    getAll(): Observable<User[]> {
        return this.http.get<User[]>(this.url)
    }
}
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;
    };
}
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.