"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。出于这个原因甚至更好地使用 InjectionToken
s
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);
}
// ...
}
因此 RemoteDataService
的 constructor
属性未受影响,但服务可以通过 EnvService
.
访问环境变量
在我的图书馆中,我有一个使用此代码的服务:
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。出于这个原因甚至更好地使用 InjectionToken
s
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);
}
// ...
}
因此 RemoteDataService
的 constructor
属性未受影响,但服务可以通过 EnvService
.