Angular 6+ :ProvidedIn a non root module is causing a circular dependency

Angular 6+ :ProvidedIn a non root module is causing a circular dependency

我正在尝试通过新的 providedIn 属性提供解析服务。

这是我在受保护模块中使用的翻译解析器:

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

我在受保护的路由模块中声明了翻译解析器服务:

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 { }

因为我在 translations-resolver.service.ts 中导入(打字稿导入)protected.module 以便在 providedIn 属性中使用它 我在检测到循环依赖项时收到警告:

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

protected/protected.module.ts ->

protected/protected-routing.module.ts -> 

path to translations-resolver.service.ts

由于 providedIn 属性,添加了第二条路径 (protected/protected.module.ts)。

我可以通过将 translationsResolver 作为 NgModule provider(在提供者数组中)来解决这个问题,但我更喜欢它是 injectable 提供者。

有什么解决这个问题的建议吗?

检查 angular/core 中的 forwardRef() 函数。它允许引用尚未定义的引用。

import {MyService} from './service';

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

我运行遇到了同样的问题。原来解决方案是 "don't do it",正如 Angular 中的一个人在这个帖子中解释的那样:https://github.com/angular/angular-cli/issues/10170#issuecomment-380673276

据我所知,归结为根模块提供的服务更容易摇树。

我和你一样失望。

这不是 Angular 依赖关系问题。

循环引用是由 TypeScript 编译器在尝试解析循环 imports.

时生成的

第一个解决方案

创建一个名为 ProtectedResolversModule 的新模块并使用 providedIn: ProtectedResolversModule 并将解析器移到那里。

现在您可以将该模块导入 ProtectedModule 并且在加载 ProtectedRoutingModule 时不会出现循环依赖错误。

第二种解决方案

使用 ProtectedModuleproviders 数组。

更新 - 2019 年 10 月

我现在收到了 5 个 up-votes 的回答,所以我觉得我应该坦白说我实际上不再遵循我自己对此的建议(如下)!

由于官方(并广泛遵循)Angular 政策是使用 providedIn: 'root',我决定如果我坚持这样做,总体上不会让其他开发人员感到困惑。到目前为止,它没有给我带来任何问题,但下面的注意事项仍然存在,我认为保持了解这一点很重要。

原版Post

我认为 Angular 使 providedIn 语法有点混乱。似乎让很多人感到困惑。例如。查看这两个 github 线程:

providedIn 语法似乎有两个主要的好处:

  1. 它支持 tree-shaking 个未使用的服务
  2. providedIn: 'root' 确保您只会获得一个服务实例

但是如果你正在写一个 library 而不是一个 application 你只需要 (1) (因为你为什么要包括您在应用程序中不需要的服务),并且只需确保不多次导入服务模块即可避免多个服务实例 (2)。

使用 providedIn 语法的 问题 是:

  1. providedIn: 'root' 打破了服务和它“存在”(或“存在”)的模块之间的 link - 因为服务不知道模块,模块也不知道'不知道这项服务。这意味着该服务不再真正“属于”该模块,而只会与引用它的任何内容捆绑在一起。这反过来意味着现在由服务 consumer 来确保服务的可注入依赖项(如果有的话)在使用之前可用,这令人困惑且相当 counter-intuitive(当然除非依赖项——以及它们的依赖项等——也都是 providedIn: 'root',在这种情况下它们会自行处理)。
  2. 上面描述的循环引用问题。实际上 不可能 - 通过这种语法 - 要保留服务及其模块之间的 link, 如果服务实际上被任何人使用同一模块中的组件.

这与官方 Angular 指南相反,但我的建议是:不要使用 providedIn,除非您正在编写需要 tree-shaking - 在模块上使用旧的(未弃用的)providers 语法,即:

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

Angular9+

您可以使用 providerIn: Any

基本上它就像模块一样工作,但是你不直接使用模块所以没有更多的循环依赖

文档:https://angular.io/api/core/Injectable#options

'any' : Provides a unique instance in each lazy loaded module while all eagerly loaded modules share one instance.

换句话说,它在不同的注入树中。它与您在其他模块中使用的实例不同。

更多参考资料

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

'Any' is very helpful to make sure a service is a singleton within module boundaries. It's a robust alternative to 'root' to make sure the individual modules don't have a side effect on each other.

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

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