确保实例化服务

时间:2016-12-19 03:04:45

标签: angular dependency-injection angular-services

背景

我们正在构建一个Angular2应用程序,并且正在积累与一个模块相关的许多特定服务。所有这些服务都松散地耦合到应用程序中的Subject<Type>事件系统。

通过构造函数实例化

因为这些服务永远不会被直接引用,只能订阅事件,所以我们只需要以某种方式实例化它们。目前我们只是将它们注入到另一个使用的服务的构造函数中。

// Services not used, just to make sure they're instantiated
constructor(
  private appService1: AppService1,     
  private appService2: AppService2,
  private appService3: AppService3,
  ...
){ }

这看起来有点像黑客,是否有更好的方法来显式声明需要实例化的服务而不通过构造函数注入它们?

3 个答案:

答案 0 :(得分:6)

根据您的喜好确定服务实例化的另一种方式可以是将其注入到模块构造器中

这样,服务将与模块一起实例化。

因此,与其像您在问题中描述的那样创建一个全新的服务,而不仅仅是为了向其中注入其他服务,而是可以这样做:

@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的某些部分。

答案 1 :(得分:2)

正如各种评论中所提到的,一个选项只是直接实例化这些服务,这看起来像

// 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这样的加载器。

也就是说,如果你发现自己经常这样做非常,你可能需要重新评估你的应用程序结构,但总的来说有很多用例,这个解决方案是最简单的。 此外,它可以打破喷射器中的循环。

通过注入器中的中断循环,我的意思是可以捕获所需服务的实例,否则需要将其注入另一个服务的构造函数中,只需在为{{1}指定的表达式中引用它的值}}。这项技术对useValue更有用。无论如何,这是相当罕见的,但可以是一个有用的解决方法。

答案 2 :(得分:0)

提供服务时会对其进行实例化。

您在构造函数中正在执行的操作是将现有服务实例注入到类中,以便可以使用它。

实例化或提供服务有三种方法:

一个是在app模块中。

@NgModule ({
    providers: [ myservice1, myservice2],
})

这会实例化服务并使其在整个应用程序中可用。

您也可以在功能模块或共享模块中提供它,它可以完成同样的事情。如果您在应用程序模块中提供它,然后在延迟加载的功能模块中提供相同的服务,它将实例化全局可用的第二个服务。第一个消失在以太中。

如果要为特定组件实例化一个服务,并且每个组件可能有多个实例,那么您可以在组件元数据中提供它。

@Component ({
     providers: [myservice]
})

然后你将使用构造函数注入它。