我正在处理一个项目,遇到了一个没有@Injectable()装饰器的服务,它运行良好。到目前为止,我的印象是,在angular中,如果我们想实现DI,我们必须使用@injectable()装饰器,并使用提供程序对其进行配置。使用提供程序配置服务是强制性的,但使用@injectable()decorator似乎不是。它只在某些服务中使用。我注意到,在使用decorator的服务和不使用decorater的服务中,唯一的区别是前者本身有一些依赖关系,而后者没有
我有两种类型的服务:
类型1:
export class SharedService {
//do something
}
类型2:
@Injectable()
export class MainCreditService {
constructor(private http: Http, private config: Config) {
this.config.EndPoints.subscribe(endpointObj => {
this.environment = endpointObj;
});
}
app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { FormsModule, ReactiveFormsModule} from '@angular/forms';
import { HttpClientModule} from '@angular/common/http'
@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
HttpClientModule,
FormsModule,
ReactiveFormsModule,
AppRoutingModule,
],
exports: [],
providers: [MainService,SharedService],
bootstrap: [AppComponent]
})
export class AppModule { }
app.component.ts
import { Component } from '@angular/core';
import { HttpClient } from "@angular/common/http";
import { MainService } from '../../services/main.service';
import { SharedService } from '../../services/shared.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
constructor(private mainservice: MainService, private sharedService:
SharedService) {
//doSomething
}
为什么一个需要@Injectable()而另一个不需要
我看了一些关于DI的视频,阅读了一些文章,但仍然很难理解我们应该在哪里使用这个装饰器以及为什么。有人能解释一下这个装饰器的用途吗?或者把链接粘贴到解释得很好的源代码上。
是的,您说得对,我们可以在没有Injectable()装饰器的情况下创建服务。那我们为什么需要这个?
可注入装饰器或任何其他角度/自定义装饰器生成元数据。注入服务需要一种特殊类型的元数据(design:paramtypes)。
无依赖项且无可注入项:
@Component({
selector: 'ponyracer-app',
template: '<h1>PonyRacer</h1>'
})
export class PonyRacerAppComponent {
constructor(private appService: AppService) {
console.log(appService);
}
}
// without Injectable
export class AppService {
constructor() {
console.log('new app service');
}
}
以下是javascript生成的组件和服务代码。您可以看到组件生成的代码和服务生成的代码之间的一些区别。
Angular在这里添加了一些元数据,就像我们使用组件装饰器一样。一个元数据是关于appService的。但是服务代码没有元数据。
var AppService = (function () {
function AppService() {
console.log('new app service');
}
return AppService;
}());
exports.AppService = AppService;
var PonyRacerAppComponent = (function() {
function PonyRacerAppComponent(appService) {
this.appService = appService;
console.log(appService);
}
PonyRacerAppComponent = __decorate([
core_1.Component({
selector: 'ponyracer-app',
template: '<h1>PonyRacer</h1>'
}),
__metadata('design:paramtypes', [app_service_1.AppService])
], PonyRacerAppComponent);
return PonyRacerAppComponent;
}());
那么,如果我们的服务依赖于其他服务,比如下面的HttpService,会发生什么呢?
export class AppService {
constructor(http: HttpService) {
console.log(http);
}
}
以下是生成的服务代码:
var AppService = (function() {
function AppService(http) {
console.log(http);
}
return AppService;
}());
exports.AppService = AppService;
Angular没有提供有关http变量的信息,当我们运行此代码时,它将给出一个错误,如错误:无法解析AppService的所有参数:(?)。
现在,如果这个服务是用Injectable()修饰的,或者即使有任何自定义的空装饰器,它也会为http服务元数据创建引用。
var AppService = (function() {
function AppService(http) {
console.log(http);
}
AppService = __decorate([
core_1.Injectable(),
__metadata('design:paramtypes', [http_service_1.HttpService])
], AppService);
return AppService;
}());
exports.AppService = AppService;
从文档中:https://angular.io/guide/dependency-injection
通过在以下三个位置之一设置元数据值,您可以在应用程序的不同级别使用提供者配置注射器:
-
在服务本身的@Injectable()装饰器中。
-
在NgModule的@NgModule()装饰器中。
-
在组件的@Component()装饰器中。
@Injectable()decorator具有providedIn元数据选项,在该选项中,您可以使用根注入器或特定NgModule的注入器指定装饰服务类的提供程序。
要使用带有DI的类,它必须有一个提供程序,而@Injectable()decorator是注册提供程序的一种方法。
如果使用@Injectable()
,则不必将MyServiceClass显式添加到模块或组件的providers属性中。
In Summary@Injectable()不是强制性的,因为它只是定义提供程序的另一种方式。同样,如果将@Injectable({providedIn: 'root'})
添加到服务中,则不需要将该服务添加到任何模块或组件的任何providers
属性中。
小示例
假设您有一个组件:
@Component({
selector: "service-tester",
templateUrl: "./service-tester.component.html",
styleUrls: ["./service-tester.component.css"],
providers: [ProvidedByComponentService]
})
export class ServiceTesterComponent {
constructor(
public componentService: ProvidedByComponentService,
public rootService: ProvidedByRootService
) {}
}
它注入了两个服务:一个在根级别,另一个在组件中提供。
如果我们看看我们的服务,他们会做同样的事情:
export class ProvidedByComponentService {
public clickCount: BehaviorSubject<number>;
constructor() {
this.clickCount = new BehaviorSubject<number>(0);
}
increment() {
this.clickCount.next(this.clickCount.value + 1);
}
}
唯一的区别是根目录中提供的服务有一个装饰器:
@Injectable({ providedIn: "root" })
export class ProvidedByRootService {
// same as previous service, code omitted for brevity.
}
现在,这些服务之间的唯一区别是,当向模板中添加新的<service-tester>
组件时,提供者将创建多少实例。
- ProvidedByComponentService:将为每个ServiceTesterComponent创建一个新实例
- ProvidedByRootService:将只创建一个实例
这意味着当计数在ProvidedByRootService
中递增时,它将在每个ServiceTesterComponent
中递增,而ProvidedByComponentService
中的计数将仅在该组件内本地递增
这允许您以灵活的方式声明提供者,具体取决于服务是否可以是单例服务。
服务(如API服务)是可以从根目录中提供的服务中受益的很好的示例,因为它们通常不保持任何状态,或保持与整个应用程序相关的状态。
以下示例的堆栈闪电战:
https://stackblitz.com/edit/angular-ivy-jboftq?file=src/app/service-测试仪/服务-测试组件.ts
Injectable()装饰器用于将其他服务或对象注入到您的服务中。
如果您的服务没有任何依赖项,那么就不需要添加Injectable()decorator。但是,如果您的服务有一些依赖项,那么您必须添加Injectable()decorator,否则angular将无法在您的服务中注入依赖项,并且会引发运行时错误。
您有两个服务,一个带有Injectable()decorator,另一个没有Injectble()。
您会注意到MainCreditService有Injectable()decorator,它在构造函数中也有一些依赖项,即private http:http、private config:config。但是SharedService没有Injectable()装饰器,也没有依赖项。
export class SharedService {
//do something
}
@Injectable()
export class MainCreditService {
constructor(private http: Http, private config: Config) {
this.config.EndPoints.subscribe(endpointObj => {
this.environment = endpointObj;
});
如果使用@Injectable()
装饰器通过将provideIn
设置为root
值来装饰服务,则它将在整个应用程序中创建单例实例,并将相同的实例用于您注入的所有组件。例如:
class HomeComponent {
constructor(private userService: UserService) {}
}
请参阅链接:https://angular.io/guide/dependency-injection#injector-层次结构和服务实例