背景
我们正在构建一个Angular2应用程序,并且正在积累与一个模块相关的许多特定服务。所有这些服务都与应用程序中的Subject<Type>
事件系统松散耦合。
通过构造函数实例化
因为这些服务永远不会被直接引用,只能订阅事件,所以我们只需要以某种方式实例化它们。目前我们只是将它们注入到另一个使用的服务的构造函数中。
// Services not used, just to make sure they're instantiated
constructor(
private appService1: AppService1,
private appService2: AppService2,
private appService3: AppService3,
...
){ }
这看起来有点像黑客,是否有更好的方法来显式声明需要实例化的服务而不通过构造函数注入它们?
确保服务根据您的偏好进行实例化的另一种模式可能是将其注入模块构造函数。
这样,服务就可以与模块一起实例化。
因此,如您在问题中所描述的那样,只是为了向其中注入其他服务而不是创建一个全新的服务,您可以执行以下操作:
@NgModule ({
...
})
export class SomeModule {
// Services are not used, just to make sure they're instantiated
constructor(
private appService1: AppService1,
private appService2: AppService2,
private appService3: AppService3) {
}
}
与useValue: new AppService1()
方法相比,这种方法至少有2个好处。
第一个也是最明显的一个,如果AppService1
有依赖关系,它们将由Angular的DI自动解决。
第二种,通常不是从您的应用程序中任何地方引用的服务都是全球配置服务的王者。因此,您可以将此类服务实例化与单个源文件中的全局配置相结合。这是一个例子。在这种情况下,这是NgbDatepickerConfig
服务:
import { NgModule } from "@angular/core";
import {
NgbDateAdapter, NgbDateNativeUTCAdapter, NgbDateParserFormatter, NgbDatepickerConfig, NgbDatepickerModule,
NgbDropdownModule, NgbTabsetModule
} from "@ng-bootstrap/ng-bootstrap";
import { UsDateParserFormatter } from "./us-date-parser-formatter";
@NgModule({
exports: [
NgbDatepickerModule,
NgbDropdownModule,
NgbTabsetModule
],
providers: [
{ provide: NgbDateAdapter, useClass: NgbDateNativeUTCAdapter },
{ provide: NgbDateParserFormatter, useClass: UsDateParserFormatter }
]
})
export class NgbImportsModule {
public constructor(private readonly datepickerConfigService: NgbDatepickerConfig) {
datepickerConfigService.minDate = { year: 1900, month: 1, day: 1 };
datepickerConfigService.maxDate = { year: 2099, month: 12, day: 31 };
}
}
在这个例子中,最初引入NgbImportsModule
只是为了将所需的模块从NGB重新导出到我的应用程序的其余部分。但随着我的应用程序功能的建立,NgbImportsModule
变成了一个单独的地方,NGB的某些部分可以在一个地方方便地配置。
正如各种评论中所提到的,一个选项就是直接实例化这样的服务,这看起来就像
// app.module.ts
@NgModule({
providers: [
{ provide: AppService1, useValue: new AppService1() },
{ provide: AppService2, useValue: new AppService2() },
{ provide: AppService2, useValue: new AppService3() }
]
}) export class AppModule {}
您可能想要避免直接实例化,因为它违反了所有内容都由Injector思维模式处理,但它不会因为多种原因而破坏DI或可测试性。
一个原因是ES模块的使用,结合使用可配置的加载器,以及TypeScript结构类型性质的表现力,甚至可以通过利用像SystemJS这样的加载器在运行时将这些类型的依赖项交换为测试双精度。
也就是说,如果你发现自己经常这样做,你可能需要重新评估你的应用程序结构,但总的来说有很多用例,这个解决方案是最简单的。此外,它可以打破喷射器中的循环。
通过注入器中的中断循环,我的意思是可以捕获所需服务的实例,否则需要通过在useValue
指定的表达式中引用其值来将其注入另一个服务的构造函数中。这种技术对useFactory
更有用。无论如何,这是相当罕见的,但可以是一个有用的解决方法。
提供服务时会实例化。
您在构造函数中执行的操作是将现有服务实例注入到类中,以便可以使用它。
有三种方法可以实例化或提供服务:
一个是在app模块中。
@NgModule ({
providers: [ myservice1, myservice2],
})
这会实例化服务并使其在整个应用程序中可用。
您还可以在功能模块或共享模块中提供它,它也可以完成同样的任务。如果您在应用程序模块中提供它,然后在延迟加载的功能模块中提供相同的服务,它将实例化全局可用的第二个服务。第一个消失在以太中。
如果要为特定组件实例化服务,并且可能为每个组件分配一个实例,则可以在组件元数据中提供该实例。
@Component ({
providers: [myservice]
})
然后你将使用构造函数注入它。