51 votes

Angular 6+ : L'insertion d'un module non racine provoque une dépendance circulaire

J'essaie de fournir un service de résolution via le nouvel outil providedIn attribut.

Il s'agit d'un résolveur de traductions que j'utilise dans un module protégé :

import { Injectable } from '@angular/core';

import { Observable , pipe } from 'rxjs';
import {map} from "rxjs/operators";

//This is causing: "WARNING in Circular dependency detected:"
import {ProtectedModule} from "../../../protected/protected.module";

import { HttpHandlerService } from '../../http/http-handler.service';

@Injectable({
  providedIn: ProtectedModule //Over here (I need the import for this line)
})
export class TranslationsResolverService {
  constructor(private _httpHandlerService : HttpHandlerService) { }
    resolve(): any {
      //Do Something...
    }
}

J'ai déclaré le service translations resolver dans le module protected routing :

import { NgModule }           from '@angular/core';
import {RouterModule, Routes} from '@angular/router';

import {AuthGuard} from "../core/resolvers/auth/auth.guard";
import {TranslationsResolverService} from "./../core/resolvers/translations/translations-resolver.service";

const routes: Routes = [
  {
    path : 'app' ,
    component: ProtectedComponent,
    resolve : {
      translations : TranslationsResolverService // <---- Over here - i can't remove that of course
    },
    canActivate: [AuthGuard],
    ]
  }
];

@NgModule({
  imports : [RouterModule.forChild(routes)],
  exports : [RouterModule]
})
export class ProtectedRoutingModule { }

En raison du fait que j'importe (import de typescript) le fichier protected.module dans le translations-resolver.service.ts afin de l'utiliser dans le providedIn attribut Je reçois un WARNING dans Dépendance circulaire détectée :

path/to/translations-resolver.service.ts -> 

protected/protected.module.ts ->

protected/protected-routing.module.ts -> 

path to translations-resolver.service.ts

Le 2e chemin (protected/protected.module.ts) est ajouté en raison de l'utilisation de l'option providedIn attribut.

Je peux résoudre ce problème en fournissant simplement le translationsResolver en tant que NgModule provider (dans le tableau des fournisseurs) mais je préfère que ce soit un injectable fournisseur.

Des suggestions pour résoudre ce problème ?

0 votes

Vous devez toujours fournir votre service dans l'injecteur Root, sauf si vous souhaitez que le service ne soit disponible que si le consommateur importe un @NgModule particulier. Référez-vous : angular.io/guide/providers#provider-scope

0 votes

À partir d'Angular 10, vous pouvez également déclarer des fournisseurs dans des composants dont la portée est limitée à la hiérarchie de ce composant.

0 votes

En Angular 9+ vous pouvez utiliser providerIn : any, vérifiez ma réponse

28voto

DarkNeuron Points 59

J'ai rencontré le même problème. Il s'avère que la solution est "ne le faites pas", comme expliqué dans ce fil de discussion par l'un des gars d'Angular : https://github.com/angular/angular-cli/issues/10170#issuecomment-380673276

Cela se résume au fait qu'il est plus facile de secouer l'arborescence des services lorsqu'ils sont fournis par le module racine, si j'ai bien compris.

Je suis aussi déçu que vous.

0 votes

Je veux marquer cette réponse mais j'espère que quelqu'un donnera une solution à ce problème. Chaque fois que j'essaie d'utiliser un service spécifique dans un module (et non dans Root), des avertissements de dépendances circulaires apparaissent.

2 votes

C'est une mauvaise réponse de toute façon :-)

1 votes

Ce commentaire référencé a été très mal voté - en plus le commentaire dit "Avec les fournisseurs secouables en arbre, vous avez maintenant zéro raisons de fournir un service à partir d'un module (il y a un cas rare sur certains services chargés paresseux avec un composant utilisant le service d'un module paresseux séparé)". Donc ce n'est pas zéro raisons - et c'est en fait mon scénario exact.

22voto

Dan King Points 125

Mise à jour - octobre 2019

J'ai reçu 5 votes positifs pour cette réponse, alors j'ai l'impression que je dois être honnête et dire que je ne suis plus vraiment mon propre conseil à ce sujet (ci-dessous) !

Puisque la politique officielle (et largement suivie) d'Angular est d'utiliser providedIn: 'root' J'ai décidé que, dans l'ensemble, il serait moins déroutant pour les autres développeurs si je m'en tenais à cela. Jusqu'à présent, cela ne m'a causé aucun problème, mais les avertissements ci-dessous demeurent et je pense qu'il est important d'en être conscient.

Poste original

Je pense qu'Angular a fait un peu n'importe quoi avec le providedIn la syntaxe. Il semble que cela ait dérouté beaucoup de personnes. Voir par exemple ces deux fils de discussion sur Github :

Le site providedIn La syntaxe semble avoir 2 principaux avantages :

  1. Il prend en charge l'arborescence des services non utilisés
  2. providedIn: 'root' garantit que vous n'aurez jamais qu'une seule instance du service.

Mais vous n'avez réellement besoin de (1) que si vous écrivez une bibliothèque plutôt qu'un application (car pourquoi incluriez-vous un service dont vous n'avez pas besoin dans votre application), et vous pouvez éviter les instances de service multiples (2) en veillant simplement à ne pas importer le module de service plus d'une fois.

Le site problèmes avec le providedIn syntaxe sont :

  1. providedIn: 'root' rompt le lien entre le service et le module dans lequel il "vit" (ou "avec"), car le service ne connaît pas le module et le module ne connaît pas le service. En effet, le service ne connaît pas le module et le module ne connaît pas le service. Cela signifie que le service n'appartient plus vraiment à ce module et qu'il est simplement regroupé avec ce qui le référence. Cela signifie à son tour qu'il appartient maintenant au service de consommateur pour s'assurer que les dépendances injectables du service (s'il en a) sont disponibles avant qu'il ne soit utilisé, ce qui est déroutant et assez contre-intuitif (à moins bien sûr que les dépendances - et leurs dépendances etc. - soient également toutes providedIn: 'root' dans ce cas, ils se débrouilleront tout seuls).
  2. Le problème de référence circulaire décrit ci-dessus. En fait, il ne s'agit pas possible - via cette syntaxe - pour que le lien entre le service et son module soit préservé, si le service est effectivement utilisé par des composants du même module .

Ceci est contraire aux conseils officiels d'Angular, mais mon conseil serait le suivant : N'utilisez pas providedIn à moins que vous n'écriviez une bibliothèque tierce qui nécessite de secouer les arbres. - utiliser l'ancienne méthode (non dépréciée) providers sur le module à la place, c'est-à-dire :

@NgModule({ providers: [MyService], })

4 votes

Vous devriez placer l'addendum en haut de votre réponse, comme suit Update Oct 2019 pour que les gens le lisent d'abord avant votre ancienne réponse. (Cela évite les lectures inutiles et la confusion)

0 votes

Utilisation de providedIn : any, après Angular 9

17voto

Mathew Foscarini Points 6076

Ce n'est pas un problème de dépendances Angular.

La référence circulaire est générée par le compilateur TypeScript lorsqu'il tente de résoudre le problème de la référence circulaire. importations .

Première solution

Créez un nouveau module nommé ProtectedResolversModule et utiliser providedIn: ProtectedResolversModule et y déplacer les résolveurs.

Vous pouvez maintenant importer ce module dans ProtectedModule et vous n'obtiendrez pas d'erreur de dépendance circulaire au moment de charger ProtectedRoutingModule .

Deuxième solution

Utilisez le providers de ProtectedModule .

5 votes

Je n'ai pas compris la première solution. Pouvez-vous montrer du code s'il vous plaît ?

9 votes

La deuxième solution la rend inarticulable.

0 votes

Angular 9 a une autre solution, n'importe laquelle.

2voto

maxisam Points 8843

Dans Angular9+

vous pouvez utiliser providerIn : Tout

En fait, il fonctionne comme le module, mais vous n'utilisez pas le module directement, donc plus de dépendance circulaire.

Doc : https://angular.io/api/core/Injectable#options

'any' : Fournit une instance unique dans chaque module chargé paresseusement tandis que tous les modules chargés avidement partagent une instance.

En d'autres termes, il se trouve dans un arbre d'injection différent. Ce n'est pas la même instance que vous avez utilisée dans un autre module.

Plus d'arbitres

https://dev.to/christiankohler/improved-dependeny-injection-with-the-new-providedin-scopes-any-and-platform-30bb

Any" est très utile pour s'assurer qu'un service est un singleton dans les limites du module. C'est une alternative robuste à "Root" pour s'assurer que les modules individuels n'ont pas d'effet secondaire les uns sur les autres.

https://indepth.dev/posts/1151/a-deep-dive-into-injectable-and-providedin-in-ivy

Code pour providerIn

  private injectableDefInScope(def: ɵɵInjectableDef<any>): boolean {
    if (!def.providedIn) {
      return false;
    } else if (typeof def.providedIn === 'string') {
      return def.providedIn === 'any' || (def.providedIn === this.scope);
    } else {
      return this.injectorDefTypes.has(def.providedIn);
    }
  }

0voto

Ozgur GUL Points 927

Vérifiez forwardRef() de la fonction angular/core. Elle permet de se référer à des références qui ne sont pas encore définies.

import {MyService} from './service';

constructor(@Inject(forwardRef(() => MyService)) public myService: MyService) {
}

0 votes

Ozgur je ne suis pas sûr que cela puisse m'aider dans mon cas à résoudre la dépendance circulaire.

0 votes

Au lieu d'injecter votre service dans les Routes, utilisez le constructeur.

0 votes

Je dois "injecter" (register est plus correct) le résolveur dans les routes pour la phase de résolution. et je veux utiliser la syntaxe providedIn afin de marquer la portée du service résolveur. Lequel d'entre eux dois-je supprimer / modifier selon votre solution ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X