【问题标题】:Angular 6+ :ProvidedIn a non root module is causing a circular dependencyAngular 6+:在非根模块中提供导致循环依赖
【发布时间】:2018-12-06 07:36:46
【问题描述】:

我正在尝试通过新的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

第二个路径(protected/protected.module.ts)由于providedIn属性而被添加。

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

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

【问题讨论】:

  • 您应该始终在根注入器中提供您的服务,除非您希望服务仅在消费者导入特定的@NgModule 时才可用。参考:angular.io/guide/providers#provider-scope
  • 从 Angular 10 开始,您还可以在仅适用于该组件层次结构的组件中声明提供程序。
  • 在 Angular 9+ 中,您可以使用 providerIn: any,查看我的答案

标签: angular typescript angular6 angular7


【解决方案1】:

我遇到了同样的问题。原来解决方案是“不要这样做”,正如其中一位 Angular 人员在此线程中所解释的那样:https://github.com/angular/angular-cli/issues/10170#issuecomment-380673276

据我所知,当它们由根模块提供时,服务更容易摇树。

我和你一样失望。

【讨论】:

  • 我想标记这个答案,但我希望有人会为这个问题提供解决方案。每次我尝试在模块范围(而不是根)中使用特定服务时都会抛出一些循环依赖警告。
  • 这是一个糟糕的答案:-)
  • 该引用的评论被严重否决 - 加上评论说“使用可摇树的提供程序,您现在有零理由从模块提供服务(在一些延迟加载的服务上很少见)使用来自单独惰性模块的服务的组件)。”所以这不是零原因 - 实际上是我的确切情况。
  • 确实令人失望,尽管我不得不说这并没有给我带来任何问题。
  • 使用 providedIn: any,在 Angular 9 之后,查看我的答案
【解决方案2】:

更新 - 2019 年 10 月

我现在已经收到 5 票赞成这个答案,所以我觉得我应该坦白说我实际上不再遵循我自己的建议(下)!

由于官方(并被广泛遵循)Angular 政策是使用providedIn: 'root',因此我决定总体而言,如果我坚持这一点,其他开发人员不会感到困惑。到目前为止,它还没有给我带来任何问题,但是下面的警告仍然存在,我相信保持意识到这一点很重要。

原帖

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

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

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

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

providedIn 语法的问题是:

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

这与 Angular 官方指南相反,但我的建议是:不要使用 providedIn,除非您正在编写需要摇树的第三方库 - 使用旧的 (不推荐使用)providers 模块上的语法,即:

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

【讨论】:

  • 您应该将附录作为Update Oct 2019 之类的形式放在答案的顶部,以便人们在您的旧答案之前先阅读它。 (节省不必要的阅读和混乱)?
  • 使用providedIn:任何,在Angular 9之后
  • @maxisam any 不是为了那个。
  • @maxisam 你的回答帮助我更好地理解了哈哈。谢谢你..检查我的新评论?
  • @maxisam 是的,你是对的 :)。我在解析器中专门面对过很多次。有道理,使用 any 将帮助 OP 实现他想要的。但是大多数答案都在谈论不是 OP 要求而是替代方案。
【解决方案3】:

这不是 Angular 依赖问题。

循环引用由 TypeScript 编译器在尝试解析循环 imports 时生成。

第一个解决方案

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

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

第二种解决方案

使用ProtectedModuleproviders 数组。

【讨论】:

  • 我没有得到第一个解决方案,请您显示一些代码。
  • 第二种解决方案使其不可摇晃
  • Angular 9 有另一个解决方案,any.
【解决方案4】:

在 Angular9+ 中

你可以使用 providerIn: Any

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

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

'any' :在每个延迟加载的模块中提供一个唯一的实例,而所有急切加载的模块共享一个实例。

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

更多参考

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

'Any' 非常有助于确保服务是模块边界内的单例。它是 'root' 的强大替代方案,可确保各个模块不会对彼此产生副作用。

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

【讨论】:

    【解决方案5】:

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

    import {MyService} from './service';
    
    constructor(@Inject(forwardRef(() => MyService)) public myService: MyService) {
    }
    

    【讨论】:

    • Ozgur 我不确定这如何帮助我解决循环依赖问题。
    • 不要在路由中注入你的服务,而是使用构造函数。
    • 我必须在解析阶段的路由中“注入”(注册更正确)解析器。我想使用 providedIn 语法来标记解析器服务的范围。根据您的解决方案,我应该删除/更改其中的哪一个?
    • 这不好。只要有import声明,问题就一直没有解决。
    猜你喜欢
    • 2023-03-13
    • 2019-09-09
    • 2018-07-22
    • 2022-01-09
    • 1970-01-01
    • 2017-10-09
    • 2021-04-19
    • 1970-01-01
    • 2015-04-17
    相关资源
    最近更新 更多