"Can't resolve all parameters for service: (?)" 当我尝试在 Angular 10 中使用图书馆的服务时

"Can't resolve all parameters for service: (?)" when I try to use service from library in Angular 10

在我的图书馆中,我有一个使用此代码的服务:

import { Inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { DataInjectorModule } from '../../data-injector.module';

// @dynamic
@Injectable()
export class RemoteDataService<T> {
  @Inject('env') private environment: any = {};
  public type: new () => T;
  
  constructor(
    model: T,
  ) {
    this.http = DataInjectorModule.InjectorInstance.get(HttpClient);
  }

  // ...
}

data-injector.module(存在的原因只是为了避免循环依赖):

import { NgModule, Injector } from '@angular/core';

// @dynamic
@NgModule({
  declarations: [],
  imports: [],
  providers: [],
  exports: [],
})
export class DataInjectorModule {
  static InjectorInstance: Injector;

  constructor(injector: Injector) {
    DataInjectorModule.InjectorInstance = injector;
  }

}

在我的库的主模块文件中:

import { ModuleWithProviders } from '@angular/compiler/src/core';
import { NgModule, Injector } from '@angular/core';
import { DataInjectorModule } from './data-injector.module';
import { RemoteDataService } from './services/remote-data/remote-data.service';

// @dynamic
@NgModule({
  declarations: [],
  imports: [
    DataInjectorModule,
  ],
  providers: [],
  exports: [],
})
export class DataCoreModule {
  static InjectorInstance: Injector;

  constructor(injector: Injector) {
    DataCoreModule.InjectorInstance = injector;
  }

  public static forRoot(environment: any): ModuleWithProviders {
    return {
      ngModule: DataCoreModule,
      providers: [
        RemoteDataService,
        { provide: 'env', useValue: environment }
      ]
    };
  }
}

终于在我的应用程序中 app.module.ts:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { DataCoreModule } from 'data-core';

import { AppRoutingModule } from './app-routing.module';
import { environment } from 'src/environments/environment';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    DdataCoreModule.forRoot(environment),
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

构建顺利,但在浏览器中出现此错误:

Error: Can't resolve all parameters for RemoteDataService: (?).
    at getUndecoratedInjectableFactory (core.js:11338)
    at injectableDefOrInjectorDefFactory (core.js:11328)
    at providerToFactory (core.js:11371)
    at providerToRecord (core.js:11358)
    at R3Injector.processProvider (core.js:11256)
    at core.js:11230
    at core.js:1146
    at Array.forEach (<anonymous>)
    at deepForEach (core.js:1146)
    at R3Injector.processInjectorType (core.js:11230)

我在 Whosebug 中检查了几个关于这个主题的问题,比如 但几乎所有地方都缺少 @Injectable(),但在这种情况下我使用这个装饰器。

知道如何解决这个问题吗?

Angular 大多数情况下,DI 在 constructor 中完成。正如您编写的这样的构造函数

constructor(
    model: T,
  )

Angular 认为您正在尝试注入 T。你应该在构造函数中注入所有你想要的东西

constructor( @Inject('env') private environment) {}

但请确保正确提供了 env。出于这个原因甚至更好地使用 InjectionTokens

Error: Can't resolve all parameters for RemoteDataService: (?)

错误说明了一切。 Angular 无法解析 RemoteDataService 中的所有构造函数参数。实例化此服务时,它需要所需的参数。

您可以通过 InjectionToken, please see this 答案提供所需的依赖项以获取详细信息。

但是您的服务使用 generics 并且您没有提及您如何在您的组件中使用该服务,所以我建议您在您的组件中声明提供者(或者模块也可以工作)并且使用 @Inject() 在您的组件中注入此服务的不同版本,如下所示(签出 this StackBlitz 并查看来自服务构造函数的日志的控制台)-

import { Component, Inject } from "@angular/core";
import { RemoteDataService } from "./custom/remote-data.service";

export class A {}

export class B {}

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"],
  providers: [
    { provide: "env", useValue: {} },
    {
      provide: "ARemoteDataService",
      useFactory: () => new RemoteDataService<A>(new A())
    },
    {
      provide: "BRemoteDataService",
      useFactory: () => new RemoteDataService<B>(new B())
    }
  ]
})
export class AppComponent {
  constructor(
    @Inject("ARemoteDataService")
    private aRemoteDataService: RemoteDataService<A>,
    @Inject("BRemoteDataService")
    private bRemoteDataService: RemoteDataService<B>
  ) {}
}

此外,不确定是否可以在构造函数外部使用 @Inject()。但是您始终可以使用注入器来获取其他依赖项(在您的情况下为env)-

// @Inject('env') private environment: any = {};
constructor(model: T) {
this.environment = DataInjectorModule.InjectorInstance.get("env");

}

我找到了解决方案(实际上是一种解决方法)。我认为这不是一种优雅的方式,但它确实有效。

我创建了一个 EnvService class 可以从模块中获取 environment 参数的东西,并且没有构造函数属性的副作用:

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

@Injectable({
  providedIn: 'root',
})
export class EnvService {
  public environment: any = {};

  constructor(@Inject('env') private env?: any) {
    this.environment = env ?? {};
  }
}

然后在我的库的主模块文件中我设置了 EnvService 而不是 RemoteDataService:

import { ModuleWithProviders } from '@angular/compiler/src/core';
import { NgModule, Injector } from '@angular/core';
import { DataInjectorModule } from './data-injector.module';
import { EnvService } from './services/env/env.service';

// @dynamic
@NgModule({
  declarations: [],
  imports: [
    DataInjectorModule,
  ],
  providers: [],
  exports: [],
})
export class DataCoreModule {
  static InjectorInstance: Injector;

  constructor(injector: Injector) {
    DataCoreModule.InjectorInstance = injector;
  }

  public static forRoot(environment: any): ModuleWithProviders {
    return {
      ngModule: DataCoreModule,
      providers: [
        EnvService,
        { provide: 'env', useValue: environment }
      ]
    };
  }
}

最后在我的 RemoteDataService 中将 @Inject 解决方案更改为 InjectorInstance.get(EnvService) 解决方案:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { DataInjectorModule } from '../../data-injector.module';
import { EnvService } from '../env/env.service';

// @dynamic
@Injectable()
export class RemoteDataService<T> {
  private env = DdataInjectorModule.InjectorInstance.get(EnvService);
  public type: new () => T;
  
  constructor(
    model: T,
  ) {
    this.http = DataInjectorModule.InjectorInstance.get(HttpClient);
  }

  // ...
}

因此 RemoteDataServiceconstructor 属性未受影响,但服务可以通过 EnvService.

访问环境变量