NgModules > Services Singleton

Références

L'actualité

Librairie

L'information

Fournir un service singleton

Il existe deux manières de transformer un service en singleton avec Angular :
  • Déclarez que le service doit être fourni dans la racine de l'application.
  • Inclure le service dans le AppModule ou dans un module importé uniquement par le AppModule.
À partir de Angular 6.0, la méthode préférée pour créer un service singleton est de spécifier au service qu'il doit être fourni à la racine de l'application. Ceci est fait en mettant providedIn sur root du décorateur du service @Injectable


forRoot()

Si un module fournit à la fois des fournisseurs et des déclarations (composants, directives, canaux), le charger dans un injecteur enfant, tel qu'une route, dupliquerait les instances de fournisseur. La duplication des fournisseurs poserait des problèmes car ils masqueraient les instances racine, qui sont probablement censées être des singletons. Pour cette raison, Angular offre un moyen de séparer les fournisseurs du module afin que le même module puisse être importé dans le module racine avec providers et les modules enfants sans providers.

  • Créez une méthode statique forRoot() sur le module.
  • Placez les fournisseurs dans la méthode forRoot() comme suit.

Pour rendre cela plus concret, considérons le RouterModule comme exemple. RouterModule doit fournir le service Router, ainsi que la directive RouterOutlet. RouterModule doit être importé par le module d'application racine afin que l'application dispose d'un routeur et que l'application ait au moins un routeur. Il doit également être importé par les composants de route individuels afin qu'ils puissent placer les directives RouterOutlet dans leur modèle pour les sous-routes.

Si RouterModule n'avait pas forRoot(), chaque composant de route instancierait une nouvelle instance de routeur, ce qui romprait l'application car il ne pouvait y avoir qu'un seul routeur. Pour cette raison, RouterModule a la déclaration RouterOutlet afin qu'elle soit disponible partout, mais le fournisseur de routeur se trouve uniquement dans forRoot(). Il en résulte que le module d'application racine importe RouterModule.forRoot() et obtient un routeur, alors que tous les composants de routage importent RouterModule, qui n'inclut pas le routeur.

Si vous avez un module fournissant à la fois des fournisseurs et des déclarations, utilisez ce modèle pour les séparer.

Un module qui ajoute des fournisseurs à l'application peut offrir une fonctionnalité permettant de configurer ces fournisseurs également via la méthode forRoot().

forRoot() prend un objet de configuration de service et retourne un ModuleWithProviders, qui est un objet simple avec les propriétés suivantes :

  • ngModule : dans cet exemple, la classe CoreModule.
  • providers : les fournisseurs configurés.

Plus précisément, Angular accumule tous les fournisseurs importés avant d'ajouter les éléments énumérés dans @NgModule.providers. Cette séquence garantit que tout ce que vous ajoutez explicitement aux fournisseurs a priorité sur les fournisseurs de modules importés.

Importez CoreModule et utilisez sa méthode forRoot() une fois, dans AppModule, car elle enregistre les services et vous ne souhaitez enregistrer ces services qu'une seule fois dans votre application. Si vous deviez les enregistrer plusieurs fois, vous pourriez vous retrouver avec plusieurs instances du service et une erreur d'exécution.

Vous pouvez également ajouter une méthode forRoot() dans le CoreModule qui configure le noyau UserService.

Dans l'exemple suivant, l'option, UserServiceConfig injecté, facultatif, étend le service utilisateur principal. S'il existe un UserServiceConfig, le UserService définit le nom d'utilisateur à partir de cette configuration.

Voici forRoot() qui prend un objet UserServiceConfig :

Enfin, appelez-le dans la liste imports des AppModule.

L'application affiche "Miss Marple" en tant qu'utilisateur au lieu de "Sherlock Holmes" par défaut.

N'oubliez pas d'importer en tant qu'importation CoreModule Javascript en haut du fichier; ne l'ajoutez pas à plus d'une liste @NgModule imports

Empêcher la réimportation du CoreModule

Seule la racine AppModule doit importer le fichier CoreModule. Si un module chargé paresseux l'importe également, l'application peut générer plusieurs instances d'un service. Pour vous protéger contre une réimportation de module chargé paresseux CoreModule, ajoutez le constructor CoreModule suivant :
Le constructor dit à Angular d'injecter le CoreModule dans lui-même. L'injection serait circulaire si Angular était recherché CoreModule dans l'injecteur actuel . Le décorateur @SkipSelf signifie "cherche dans CoreModule un injecteur ancêtre, au-dessus de moi dans la hiérarchie des injecteurs".

Si le constructeur s'exécute comme prévu dans l'inventaire AppModule, aucun injecteur ancêtre ne pourrait fournir une instance de CoreModule et l'injecteur devrait abandonner.

Par défaut, l'injecteur génère une erreur lorsqu'il ne peut pas trouver le fournisseur demandé. Le décorateur @Optional veut dire que le service n'est pas correct. L'injecteur retourne null, le paramètre de parentModule est null et le constructeur se termine sans incident.

Si vous importez incorrectement CoreModule dans un module chargé paresseux, tel que CustomersModule.

Angular crée un module chargé paresseux avec son propre injecteur, un enfant de l'injecteur de racine. @SkipSelf force Angular à rechercher un CoreModule dans l'injecteur parent, qui est cette fois l'injecteur racine. Bien sûr, il trouve l'instance importée par la racine AppModule. Maintenant, parentModule existe et le constructeur lève l'erreur.

Voici les deux fichiers dans leur intégralité pour référence :

app.module.ts

core.module.ts