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
时不会出现循环依赖错误。
第二种解决方案
使用 ProtectedModule
的 providers
数组。
更新 - 2019 年 10 月
我现在收到了 5 个 up-votes 的回答,所以我觉得我应该坦白说我实际上不再遵循我自己对此的建议(如下)!
由于官方(并广泛遵循)Angular 政策是使用 providedIn: 'root'
,我决定如果我坚持这样做,总体上不会让其他开发人员感到困惑。到目前为止,它没有给我带来任何问题,但下面的注意事项仍然存在,我认为保持了解这一点很重要。
原版Post
我认为 Angular 使 providedIn
语法有点混乱。似乎让很多人感到困惑。例如。查看这两个 github 线程:
providedIn
语法似乎有两个主要的好处:
- 它支持 tree-shaking 个未使用的服务
providedIn: 'root'
确保您只会获得一个服务实例
但是如果你正在写一个 library 而不是一个 application 你只需要 (1) (因为你为什么要包括您在应用程序中不需要的服务),并且只需确保不多次导入服务模块即可避免多个服务实例 (2)。
使用 providedIn
语法的 问题 是:
providedIn: 'root'
打破了服务和它“存在”(或“存在”)的模块之间的 link - 因为服务不知道模块,模块也不知道'不知道这项服务。这意味着该服务不再真正“属于”该模块,而只会与引用它的任何内容捆绑在一起。这反过来意味着现在由服务 consumer 来确保服务的可注入依赖项(如果有的话)在使用之前可用,这令人困惑且相当 counter-intuitive(当然除非依赖项——以及它们的依赖项等——也都是 providedIn: 'root'
,在这种情况下它们会自行处理)。
- 上面描述的循环引用问题。实际上 不可能 - 通过这种语法 - 要保留服务及其模块之间的 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.
换句话说,它在不同的注入树中。它与您在其他模块中使用的实例不同。
更多参考资料
'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);
}
}
我正在尝试通过新的 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
时不会出现循环依赖错误。
第二种解决方案
使用 ProtectedModule
的 providers
数组。
更新 - 2019 年 10 月
我现在收到了 5 个 up-votes 的回答,所以我觉得我应该坦白说我实际上不再遵循我自己对此的建议(如下)!
由于官方(并广泛遵循)Angular 政策是使用 providedIn: 'root'
,我决定如果我坚持这样做,总体上不会让其他开发人员感到困惑。到目前为止,它没有给我带来任何问题,但下面的注意事项仍然存在,我认为保持了解这一点很重要。
原版Post
我认为 Angular 使 providedIn
语法有点混乱。似乎让很多人感到困惑。例如。查看这两个 github 线程:
providedIn
语法似乎有两个主要的好处:
- 它支持 tree-shaking 个未使用的服务
providedIn: 'root'
确保您只会获得一个服务实例
但是如果你正在写一个 library 而不是一个 application 你只需要 (1) (因为你为什么要包括您在应用程序中不需要的服务),并且只需确保不多次导入服务模块即可避免多个服务实例 (2)。
使用 providedIn
语法的 问题 是:
providedIn: 'root'
打破了服务和它“存在”(或“存在”)的模块之间的 link - 因为服务不知道模块,模块也不知道'不知道这项服务。这意味着该服务不再真正“属于”该模块,而只会与引用它的任何内容捆绑在一起。这反过来意味着现在由服务 consumer 来确保服务的可注入依赖项(如果有的话)在使用之前可用,这令人困惑且相当 counter-intuitive(当然除非依赖项——以及它们的依赖项等——也都是providedIn: 'root'
,在这种情况下它们会自行处理)。- 上面描述的循环引用问题。实际上 不可能 - 通过这种语法 - 要保留服务及其模块之间的 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.
换句话说,它在不同的注入树中。它与您在其他模块中使用的实例不同。
更多参考资料
'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);
}
}