Angular 2 - 服务 - 来自另一个服务的依赖注入
Angular 2 - service - dependency injection from another service
我在 Angular 2 中编写了两个服务。其中一个是 Http
的基本自定义 class,其中包含一些自定义功能(目前看起来很基本,但是它将扩展):
ServerComms.ts
import {Injectable} from 'angular2/core';
import {Http} from 'angular2/http';
@Injectable()
export class ServerComms {
private url = 'myservice.com/service/';
constructor (public http: Http) {
// do nothing
}
get(options) {
var req = http.get('https://' + options.name + url);
if (options.timout) {
req.timeout(options.timeout);
}
return req;
}
}
另一个class,TicketService
利用上面这个class,调用服务中的一个方法。定义如下:
TicketService.ts
import {Injectable} from 'angular2/core';
import {ServerComms} from './ServerComms';
@Injectable()
export class TicketService {
constructor (private serverComms: ServerComms) {
// do nothing
}
getTickets() {
serverComms.get({
name: 'mycompany',
timeout: 15000
})
.subscribe(data => console.log(data));
}
}
但是,每当我尝试这个时,我都会收到以下错误:
"No provider for ServerComms! (App -> TicketService -> ServerComms)"
我不明白为什么?我当然不需要注入每个其他服务所依赖的每项服务吗?这会变得很乏味吗?这在 Angular 1.x 中是可以实现的 - 我如何在 Angular 2 中实现同样的目标?
这样做正确吗?
当然可以。
在Angular2中,有多个注入器。注入器使用 providers
组件数组配置。当组件具有 providers
数组时,将在树中的那个点创建一个注入器。当组件、指令和服务需要解析它们的依赖关系时,它们会查找注入器树来找到它们。因此,我们需要使用提供程序配置该树。
在概念上,我喜欢认为有一个注入器树覆盖在组件树上,但它比组件树更稀疏。
同样,从概念上讲,我们必须配置此注入器树,以便依赖项 "provided" 在树中的适当位置。 Angular 2 不创建单独的树,而是重用组件树来执行此操作。所以尽管感觉我们是在组件树上配置依赖,但我更愿意认为我是在注入器树上配置依赖(它恰好覆盖了组件树,所以我必须使用组件来配置它)。
一尘不染?
Angular两个有注入器树的原因是允许非单例服务——即能够在注入器树的不同点创建特定服务的多个实例。如果您想要 Angular 类似 1 的功能(仅单例服务),请在您的根组件中提供所有服务。
构建您的应用程序,然后返回并配置注入器树(使用组件)。我喜欢这样想。如果您在另一个项目中重用组件,很可能需要更改 providers
数组,因为新项目可能需要不同的注入器树。
简而言之,由于注入器是在组件级别定义的,因此发起调用服务的组件必须看到相应的提供者。第一个(直接调用)还有另一个间接调用(由之前的服务调用)。
我们来打个比方。我有以下应用程序:
组件AppComponent
:在bootstrap
函数中创建Angular2应用程序时提供的我的应用程序的主要组件
@Component({
selector: 'my-app',
template: `
<child></child>
`,
(...)
directives: [ ChildComponent ]
})
export class AppComponent {
}
组件ChildComponent
:将在AppComponent
组件
中使用的子组件
@Component({
selector: 'child',
template: `
{{data | json}}<br/>
<a href="#" (click)="getData()">Get data</a>
`,
(...)
})
export class ChildComponent {
constructor(service1:Service1) {
this.service1 = service1;
}
getData() {
this.data = this.service1.getData();
return false;
}
}
两个服务,Service1
和Service2
:Service1
被ChildComponent
使用,Service2
被[=19=使用]
@Injectable()
export class Service1 {
constructor(service2:Service2) {
this.service2 = service2;
}
getData() {
return this.service2.getData();
}
}
@Injectable()
export class Service2 {
getData() {
return [
{ message: 'message1' },
{ message: 'message2' }
];
}
}
以下是所有这些元素及其关系的概述:
Application
|
AppComponent
|
ChildComponent
getData() --- Service1 --- Service2
在这样的应用中,我们有三个注入器:
- 可以使用
bootstrap
函数的第二个参数配置的应用程序注入器
- 可以使用此组件的
providers
属性配置的 AppComponent
注入器。它可以 "see" 在应用程序注入器中定义的元素。这意味着如果在此提供程序中找不到提供程序,它将自动查找此父注入器。如果在后者中找不到,将抛出 "provider not found" 错误。
ChildComponent
注入器将遵循与 AppComponent
相同的规则。要注入组件执行的注入链中涉及的元素,将首先在此注入器中查找提供者,然后在 AppComponent
中查找,最后在应用程序中查找。
这意味着当尝试将 Service1
注入到 ChildComponent
构造函数时,Angular2 将查看 ChildComponent
注入器,然后是 AppComponent
注入器,最后进入申请一.
由于Service2
需要注入Service1
,所以会做同样的解析处理:ChildComponent
注入器,AppComponent
一个,应用一个。
这意味着 Service1
和 Service2
都可以根据您的需要使用组件的 providers
属性和 [=16= 的第二个参数在每个级别指定] 应用程序注入器的功能。
这允许共享一组元素的依赖实例:
- 如果您在应用程序级别定义提供者,则相应创建的实例将由整个应用程序(所有组件、所有服务等)共享。
- 如果您在组件级别定义提供程序,则实例将由组件本身、其子组件以及依赖链中涉及的所有 "services" 共享。
所以它非常强大,您可以根据自己的需要自由组织。
这里是相应的plunkr所以你可以玩它:https://plnkr.co/edit/PsySVcX6OKtD3A9TuAEw?p=preview.
来自 Angular2 文档的 link 可以帮助您:https://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html。
嗯,我想你应该在全球范围内提供这两种服务:
bootstrap(App, [service1, service2]);
或提供给使用它们的组件:
@Component({providers: [service1, service2]})
@Injectable
装饰器添加必要的元数据来跟踪依赖关系,但不提供它们。
我在 Angular 2 中编写了两个服务。其中一个是 Http
的基本自定义 class,其中包含一些自定义功能(目前看起来很基本,但是它将扩展):
ServerComms.ts
import {Injectable} from 'angular2/core';
import {Http} from 'angular2/http';
@Injectable()
export class ServerComms {
private url = 'myservice.com/service/';
constructor (public http: Http) {
// do nothing
}
get(options) {
var req = http.get('https://' + options.name + url);
if (options.timout) {
req.timeout(options.timeout);
}
return req;
}
}
另一个class,TicketService
利用上面这个class,调用服务中的一个方法。定义如下:
TicketService.ts
import {Injectable} from 'angular2/core';
import {ServerComms} from './ServerComms';
@Injectable()
export class TicketService {
constructor (private serverComms: ServerComms) {
// do nothing
}
getTickets() {
serverComms.get({
name: 'mycompany',
timeout: 15000
})
.subscribe(data => console.log(data));
}
}
但是,每当我尝试这个时,我都会收到以下错误:
"No provider for ServerComms! (App -> TicketService -> ServerComms)"
我不明白为什么?我当然不需要注入每个其他服务所依赖的每项服务吗?这会变得很乏味吗?这在 Angular 1.x 中是可以实现的 - 我如何在 Angular 2 中实现同样的目标?
这样做正确吗?
当然可以。
在Angular2中,有多个注入器。注入器使用 providers
组件数组配置。当组件具有 providers
数组时,将在树中的那个点创建一个注入器。当组件、指令和服务需要解析它们的依赖关系时,它们会查找注入器树来找到它们。因此,我们需要使用提供程序配置该树。
在概念上,我喜欢认为有一个注入器树覆盖在组件树上,但它比组件树更稀疏。
同样,从概念上讲,我们必须配置此注入器树,以便依赖项 "provided" 在树中的适当位置。 Angular 2 不创建单独的树,而是重用组件树来执行此操作。所以尽管感觉我们是在组件树上配置依赖,但我更愿意认为我是在注入器树上配置依赖(它恰好覆盖了组件树,所以我必须使用组件来配置它)。
一尘不染?
Angular两个有注入器树的原因是允许非单例服务——即能够在注入器树的不同点创建特定服务的多个实例。如果您想要 Angular 类似 1 的功能(仅单例服务),请在您的根组件中提供所有服务。
构建您的应用程序,然后返回并配置注入器树(使用组件)。我喜欢这样想。如果您在另一个项目中重用组件,很可能需要更改 providers
数组,因为新项目可能需要不同的注入器树。
简而言之,由于注入器是在组件级别定义的,因此发起调用服务的组件必须看到相应的提供者。第一个(直接调用)还有另一个间接调用(由之前的服务调用)。
我们来打个比方。我有以下应用程序:
组件
AppComponent
:在bootstrap
函数中创建Angular2应用程序时提供的我的应用程序的主要组件@Component({ selector: 'my-app', template: ` <child></child> `, (...) directives: [ ChildComponent ] }) export class AppComponent { }
组件
中使用的子组件ChildComponent
:将在AppComponent
组件@Component({ selector: 'child', template: ` {{data | json}}<br/> <a href="#" (click)="getData()">Get data</a> `, (...) }) export class ChildComponent { constructor(service1:Service1) { this.service1 = service1; } getData() { this.data = this.service1.getData(); return false; } }
两个服务,
Service1
和Service2
:Service1
被ChildComponent
使用,Service2
被[=19=使用]@Injectable() export class Service1 { constructor(service2:Service2) { this.service2 = service2; } getData() { return this.service2.getData(); } }
@Injectable() export class Service2 { getData() { return [ { message: 'message1' }, { message: 'message2' } ]; } }
以下是所有这些元素及其关系的概述:
Application
|
AppComponent
|
ChildComponent
getData() --- Service1 --- Service2
在这样的应用中,我们有三个注入器:
- 可以使用
bootstrap
函数的第二个参数配置的应用程序注入器 - 可以使用此组件的
providers
属性配置的AppComponent
注入器。它可以 "see" 在应用程序注入器中定义的元素。这意味着如果在此提供程序中找不到提供程序,它将自动查找此父注入器。如果在后者中找不到,将抛出 "provider not found" 错误。 ChildComponent
注入器将遵循与AppComponent
相同的规则。要注入组件执行的注入链中涉及的元素,将首先在此注入器中查找提供者,然后在AppComponent
中查找,最后在应用程序中查找。
这意味着当尝试将 Service1
注入到 ChildComponent
构造函数时,Angular2 将查看 ChildComponent
注入器,然后是 AppComponent
注入器,最后进入申请一.
由于Service2
需要注入Service1
,所以会做同样的解析处理:ChildComponent
注入器,AppComponent
一个,应用一个。
这意味着 Service1
和 Service2
都可以根据您的需要使用组件的 providers
属性和 [=16= 的第二个参数在每个级别指定] 应用程序注入器的功能。
这允许共享一组元素的依赖实例:
- 如果您在应用程序级别定义提供者,则相应创建的实例将由整个应用程序(所有组件、所有服务等)共享。
- 如果您在组件级别定义提供程序,则实例将由组件本身、其子组件以及依赖链中涉及的所有 "services" 共享。
所以它非常强大,您可以根据自己的需要自由组织。
这里是相应的plunkr所以你可以玩它:https://plnkr.co/edit/PsySVcX6OKtD3A9TuAEw?p=preview.
来自 Angular2 文档的 link 可以帮助您:https://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html。
嗯,我想你应该在全球范围内提供这两种服务:
bootstrap(App, [service1, service2]);
或提供给使用它们的组件:
@Component({providers: [service1, service2]})
@Injectable
装饰器添加必要的元数据来跟踪依赖关系,但不提供它们。