“无法解析服务的所有参数:(?)”当我尝试在 Angular 10 中使用库中的服务时

Posted

技术标签:

【中文标题】“无法解析服务的所有参数:(?)”当我尝试在 Angular 10 中使用库中的服务时【英文标题】:"Can't resolve all parameters for service: (?)" when I try to use service from library in Angular 10 【发布时间】:2021-01-31 02:32:34 【问题描述】:

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

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)

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

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

【问题讨论】:

这看起来太复杂了,我相信你可以简单地使用导入的environment 变量而不必注入它。将providedIn 属性添加到@Injectable 装饰器也比添加提供程序数组更容易。如果环境变量来自远程源,那么只需将其保存为 observable 就足够了 “使用导入的环境变量而不必注入它”部分我认为它在库中不起作用。或者它是?该库可从 npm 安装。 库中的 model: T 构造函数参数 RemoteDataService 有什么作用? @Akash 在这个例子中它什么都不做,好点。但我仍然认为这是一个重要的细节,因为一些建议的解决方案将获取环境值作为构造函数参数,我不想这样做。 我猜问题出在你的构造函数参数上。 Angular DI 需要知道您要在服务的构造函数中注入的实例。 【参考方案1】:

错误:无法解析 RemoteDataService 的所有参数:(?)

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

您可以通过InjectionToken提供所需的依赖项,详情请参阅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");

【讨论】:

【参考方案2】:

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

我创建了一个EnvService 类,它可以从模块中获取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 访问环境变量。

【讨论】:

【参考方案3】:

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

constructor(
    model: T,
  )

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

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

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

【讨论】:

model 变量用于RemoteDataSerivce 的函数中。它不能从构造函数属性中删除。而另一方面RemoteDataService在其他服务中使用,在其他服务中使用什么等等......所以我不想添加另一个构造函数属性,因为它涉及到使用该库的整个应用程序的升级。 服务实例应该通过角度依赖注入创建,而不是自定义代码

以上是关于“无法解析服务的所有参数:(?)”当我尝试在 Angular 10 中使用库中的服务时的主要内容,如果未能解决你的问题,请参考以下文章

当我尝试在 ace.js 中创建 Range 对象时,抛出“Illegal Constructor”错误

kotlin.NotImplementedError: An operation is not implemented: not implemented AlertDialog

android项目它得到一些与AN​​DROID_SDK_HOME相关的错误

MemoryError: Unable to allocate 115. GiB for an array with shape (1122, 1122, 12288) and data type f

在 Docker 容器中访问 Angular 应用程序

Android蓝牙连接问题