Injection de dépendance > Fournisseurs de dépendance

Références

L'actualité

Librairie

L'information

Fournisseurs de dépendance

Un fournisseur de dépendance configure un injecteur avec un jeton DI , que cet injecteur utilise pour fournir la version concrète d'exécution d'une valeur de dépendance. L'injecteur s'appuie sur la configuration du fournisseur pour créer des instances des dépendances qu'il injecte dans des composants, des directives, des pipes et d'autres services.

Vous devez configurer un injecteur avec un fournisseur, sinon celui-ci ne saura pas comment créer la dépendance. Le moyen le plus évident pour un injecteur de créer une instance d'une classe de service consiste à utiliser la classe elle-même. Si vous spécifiez la classe de service elle-même en tant que jeton fournisseur, le comportement par défaut consiste pour l'injecteur à instancier cette classe new.

Dans l'exemple typique suivant, la classe Logger elle-même fournit une instance Logger.

Vous pouvez toutefois configurer un injecteur avec un autre fournisseur afin de fournir un autre objet fournissant la fonctionnalité de journalisation nécessaire. Par exemple:

  • Vous pouvez fournir une classe de remplacement.
  • Vous pouvez fournir un objet semblable à un enregistreur.
  • Votre fournisseur peut appeler une fonction d'usine d'enregistreur.

L'objet littéral "Provider"

La syntaxe de fournisseur de classe est une expression abrégée qui se développe dans une configuration de fournisseur, définie par l'interface Provider. Les extraits de code suivants montrent comment une classe donnée en tant que valeur providers et développée dans un objet fournisseur complet.

La configuration de fournisseur développée est un littéral d'objet avec deux propriétés.

  • La propriété provide contient le jeton qui sert de clé pour localiser une valeur de dépendance et configurer l'injecteur.
  • La deuxième propriété est un objet de définition de fournisseur, qui indique à l'injecteur comment créer la valeur de dépendance. La clé de définition du fournisseur peut être useClass, comme dans l'exemple. Il peut aussi être useExisting, useValue ou useFactory. Chacune de ces clés fournit un type de dépendance différent, comme indiqué ci-dessous.

Fournisseurs de classe alternatifs

Différentes classes peuvent fournir le même service. Par exemple, le code suivant indique à l'injecteur de renvoyer une instance BetterLogger lorsque le composant demande un enregistreur utilisant le jeton Logger.

Fournisseurs de classe avec dépendances

Une autre classe, EvenBetterLogger pourrait afficher le nom d'utilisateur dans le message du journal. Cet enregistreur obtient l'utilisateur à partir d'une instance UserService injectée.
L'injecteur a besoin de fournisseurs pour ce nouveau service d'enregistrement et ses dépendants UserService. Configurez cet autre enregistreur avec la useClass clé de définition du fournisseur, telle que BetterLogger. Le tableau suivant spécifie les deux fournisseurs dans l'option providers de métadonnées du module ou du composant parent.


Fournisseurs de classe aliasés

Supposons qu'un ancien composant dépend de la classe OldLogger. OldLogger a la même interface que NewLogger, mais pour une raison quelconque, vous ne pouvez pas mettre à jour l'ancien composant pour l'utiliser.

Lorsque l'ancien composant enregistre un message avec OldLogger, vous voulez que l'instance singleton de NewLoggerle gère à la place. Dans ce cas, l'injecteur de dépendance doit injecter cette instance singleton lorsqu'un composant demande le nouveau ou l'ancien consignateur. OldLogger devrait être un alias pour NewLogger.

Si vous essayez d'alias OldLogger à NewLogger avec useClass, vous vous retrouvez avec deux différentes instances NewLogger dans votre application.

Pour vous assurer qu'il n'y a qu'une seule instance de NewLogger, alias OldLogger avec l'option useExisting.

Fournisseurs de valeur

Parfois, il est plus facile de fournir un objet prêt à l'emploi plutôt que de demander à l'injecteur de le créer à partir d'une classe. Pour injecter un objet que vous avez déjà créé, configurez l'injecteur avec l'option useValue.

Le code suivant définit une variable qui crée un tel objet pour jouer le rôle de consignateur.

L'objet fournisseur suivant utilise la clé useValue pour associer la variable au jeton Logger.

Dépendances hors classe

Toutes les dépendances ne sont pas des classes. Parfois, vous souhaitez injecter une chaîne, une fonction ou un objet.

Les applications définissent souvent des objets de configuration avec beaucoup de petits faits, comme le titre de l'application ou l'adresse d'un noeud final d'API Web. Ces objets de configuration ne sont pas toujours des instances d'une classe. Ils peuvent être des littéraux d'objet, comme illustré dans l'exemple suivant.

Les interfaces TypeScript ne sont pas des jetons valides

La HERO_DI_CONFIG constante est conforme à l'interface AppConfig. Malheureusement, vous ne pouvez pas utiliser une interface TypeScript en tant que jeton. Dans TypeScript, une interface est un artefact de conception et n'a pas de représentation à l'exécution (jeton) utilisable par la structure DI.


Cela peut sembler étrange si vous êtes habitué à l'injection de dépendance dans des langages fortement typés où une interface est la clé de recherche de dépendance préférée. Cependant, JavaScript n'a pas d'interface. Ainsi, lorsque l'interface TypeScript est transpilée en JavaScript, l'interface disparaît. Angular n'a plus aucune information de type d'interface à trouver au moment de l'exécution.

Une alternative consiste à fournir et à injecter l'objet de configuration dans un NgModule AppModule.

Une autre solution pour choisir un jeton de fournisseur pour les dépendances hors classe consiste à définir et à utiliser un objet InjectionToken. L'exemple suivant montre comment définir un tel jeton.
Le paramètre type, bien que facultatif, transmet le type de dépendance aux développeurs et aux outils. La description du jeton est une autre aide au développement.

Enregistrez le fournisseur de dépendance à l'aide de l'objet InjectionToken :

Maintenant, vous pouvez injecter l'objet de configuration dans n'importe quel constructeur qui en a besoin, à l'aide d'un décorateur de paramètres @Inject().
Bien que l'interface AppConfig ne joue aucun rôle dans l'injection de dépendance, elle prend en charge la saisie de l'objet de configuration dans la classe.

Fournisseurs d'usine

Parfois, vous devez créer une valeur dépendante de manière dynamique, en fonction d'informations que vous ne disposerez pas avant l'exécution. Par exemple, vous pourriez avoir besoin d'informations qui changent de manière répétée au cours de la session du navigateur. De plus, votre service injectable peut ne pas avoir un accès indépendant à la source de l'information.

Dans de tels cas, vous pouvez utiliser un fournisseur d'usine . Les fournisseurs d'usine peuvent également être utiles lors de la création d'une instance d'une dépendance à partir d'une bibliothèque tierce qui n'était pas conçue pour fonctionner avec DI.

Par exemple, supposons qu'il faille cacher les héros secrets des utilisateurs normaux. Seuls les utilisateurs autorisés doivent voir des héros secrets.

Comme EvenBetterLogger, a besoin de HeroService pour savoir si l'utilisateur est autorisé à voir des héros secrets. Cette autorisation peut changer au cours d'une session d'application unique, comme lorsque vous vous connectez à un autre utilisateur.

Supposons que vous ne souhaitiez pas effectuer d'injection directe HeroService, car vous ne souhaitiez pas compliquer ce service avec des informations confidentielles. HeroService n'aura pas d'accès direct aux informations de l'utilisateur pour décider qui est autorisé et qui ne l'est pas.

Pour résoudre ce problème, nous donnons au constructeur HeroService un drapeau booléen lui permettant de contrôler l'affichage des héros secrets.

Vous pouvez injecter Logger, mais vous ne pouvez pas injecter isAuthorized. Au lieu de cela, vous pouvez utiliser un fournisseur d'usine pour créer une nouvelle instance de consignateur HeroService.

Un fournisseur d'usine a besoin d'une fonction d'usine.

Bien qu'elle HeroService n'ait pas accès à UserService, la fonction usine le fait. Vous injectez les deux Logger et UserService chez le fournisseur de l'usine et laissez l'injecteur les transmettre à la fonction de l'usine.

  • Le champ useFactory indique à Angular que le fournisseur est une fonction fabrique dont la mise en oeuvre est heroServiceFactory.
  • La deps propriété est un tableau de jetons de fournisseur. Les classes Logger et UserService servent de jetons pour leurs propres fournisseurs de classes. L'injecteur résout ces jetons et injecte les services correspondants dans les paramètres de fonction d'usine correspondants.

Notez que vous avez capturé le fournisseur d'usine dans une variable exportée, heroServiceProvider. Cette étape supplémentaire rend le fournisseur d'usine réutilisable. Vous pouvez configurer un fournisseur de HeroService avec cette variable partout où vous en avez besoin. Dans cet exemple, vous en avez besoin uniquement dans HeroesComponent, où heroServiceProvider remplace HeroService dans le tableau providers de métadonnées.

Ce qui suit montre les nouvelles et les anciennes implémentations côte à côte.

heroes.component (v3)

heroes.component (v2)


Jetons prédéfinis et fournisseurs multiples

Angular fournit un certain nombre de constantes de jeton d'injection intégrées que vous pouvez utiliser pour personnaliser le comportement de divers systèmes.

Par exemple, vous pouvez utiliser les jetons intégrés suivants comme points d'ancrage dans le processus d'initialisation et d'initialisation de la structure. Un objet fournisseur peut associer n'importe lequel de ces jetons d'injection à une ou plusieurs fonctions de rappel prenant en charge des actions d'initialisation spécifiques à l'application.

  • PLATFORM_INITIALIZER : Le rappel est appelé lorsqu'une plate-forme est initialisée.
  • APP_BOOTSTRAP_LISTENER : Le rappel est appelé pour chaque composant démarré. La fonction de gestionnaire reçoit l'instance ComponentRef du composant amorcé.
  • APP_INITIALIZER : Le rappel est appelé avant qu'une application ne soit initialisée. Tous les initialiseurs enregistrés peuvent éventuellement renvoyer une promesse. Toutes les fonctions d'initialisation qui renvoient des promesses doivent être résolues avant le démarrage de l'application. Si l'un des initialiseurs ne parvient pas à résoudre, l'application n'est pas démarrée.
L'objet fournisseur peut avoir une troisième option, multi : true que vous pouvez utiliser APP_INITIALIZER pour enregistrer plusieurs gestionnaires pour l'événement fournisseur.

Par exemple, lorsque vous démarrez une application, vous pouvez enregistrer plusieurs initialiseurs en utilisant le même jeton.

Plusieurs fournisseurs peuvent également être associés à un seul jeton dans d'autres domaines. Par exemple, vous pouvez enregistrer un validateur de formulaire personnalisé à l'aide du jeton NG_VALIDATORS intégré et fournir plusieurs instances d'un fournisseur de validateur donné à l'aide de la multi: truepropriété de l'objet fournisseur. Angular ajoute vos validateurs personnalisés à la collection existante.

Le routeur utilise également plusieurs fournisseurs associés à un seul jeton. Lorsque vous fournissez plusieurs ensembles de routes en utilisant RouterModule.forRoot et RouterModule.forChild dans un seul module, le jeton ROUTES combine tous les différents ensembles de routes fournis en une seule valeur.

Fournisseurs d'arbres

L'arborescence fait référence à une option du compilateur qui supprime le code du dernier ensemble si ce code n'est pas référencé dans une application. Lorsque les fournisseurs sont utilisables dans l'arborescence, le compilateur Angular supprime les services associés de la sortie finale s'il détermine qu'ils ne sont pas utilisés dans votre application. Cela réduit considérablement la taille de vos paquets.

Idéalement, si une application n'injecte pas de service, elle ne devrait pas être incluse dans la sortie finale. Cependant, Angular doit pouvoir identifier au moment de la construction si le service sera requis ou non. Comme il est toujours possible d'injecter directement un service à l'aide de injector.get (Service), Angular ne peut pas identifier tous les endroits de votre code où cette injection pourrait avoir lieu. Il n'a donc pas d'autre choix que d'inclure le service dans l'injecteur. Ainsi, les services fournis au niveau de NgModule ou de composant ne peuvent pas être transformés en arborescence.

L'exemple suivant de fournisseurs non modulables dans Angular configure un fournisseur de services pour l'injecteur d'un NgModule.

Ce module peut ensuite être importé dans votre module d'application pour rendre le service disponible pour l'injection dans votre application, comme illustré dans l'exemple suivant.

Lorsque ngc est exécuté, il compile AppModule dans une fabrique de modules, qui contient les définitions de tous les fournisseurs déclarés dans tous les modules qu'il inclut. Au moment de l'exécution, cette usine devient un injecteur qui instancie ces services.

L'arborescence ne fonctionne pas ici car Angular ne peut pas décider d'exclure un bloc de code (la définition du fournisseur pour le service dans la fabrique de modules) en fonction de l'utilisation d'un autre bloc de code (la classe de service). Pour que les services soient utilisables dans l'arborescence, les informations relatives à la construction d'une instance du service (la définition du fournisseur) doivent faire partie de la classe de service elle-même.

Création de fournisseurs utilisables dans les arbres

Vous pouvez transformer un fournisseur en arborescence en le spécifiant dans le décorateur sur le service lui-même, plutôt que dans les métadonnées du composant NgModule ou du composant dépendant du service @Injectable(). L'exemple suivant montre l'équivalent en arbre de l'exemple ServiceModule ci-dessus.
Le service peut être instancié en configurant une fonction d'usine, comme dans l'exemple suivant.
Pour substituer un fournisseur utilisable dans les arbres, configurez l'injecteur d'un composant ou module Ng spécifique avec un autre fournisseur, à l'aide de la syntaxe de tableau providers : []array du décorateur @NgModule() ou @Component().