我知道如何将服务注入组件(通过@Component),但是如何使用DI来传递组件之外的服务呢?
换句话说,我不想这样做:
export class MyFirstSvc {
}
export class MySecondSvc {
constructor() {
this.helpfulService = new MyFirstSvc();
}
}
export class MyThirdSvc {
constructor() {
this.helpfulService = new MyFirstSvc();
}
}
答案 0 :(得分:46)
是的,首先要在每个要注入的服务上添加@Injectable
装饰器。事实上,Injectable
名称有点阴险。这并不意味着课程将是可注射的"但它会装饰,因此可以注入构造函数参数。有关详细信息,请参阅此github问题:https://github.com/angular/angular/issues/4404。
以下是我对注射机制的理解。为类设置@Injectable
装饰器时,Angular将尝试为当前执行链的注入器中的相应类型创建或获取实例。实际上,Angular2应用程序不仅有一个注入器,而且还有一个注入器树。它们与整个应用程序和组件隐式关联。此级别的一个关键特性是它们以分层方式链接在一起。这个注入器树映射组件树。没有为"服务"。
我们来试试吧。我有以下申请:
组件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
两个服务Service1
@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
函数AppComponent
属性配置的providers
注入器。它可以&#34;看&#34;应用程序注入器中定义的元素。这意味着如果在此提供程序中找不到提供程序,它将自动查找此父注入程序。如果在后者中找不到,则找不到&#34;提供商&#34;错误将被抛出。ChildComponent
注入器遵循与AppComponent
规则相同的规则。要注入为组件执行的注入链中涉及的元素,将首先在此注入器中查找提供者,然后在AppComponent
中查找提供者,最后在应用程序中查找提供者。这意味着当尝试将Service1
注入ChildComponent
构造函数时,Angular2会查看ChildComponent
注入器,然后进入AppComponent
注入器,最后进入申请一。
由于Service2
需要注入Service1
,因此将执行相同的解决方案处理:ChildComponent
注入器,AppComponent
一个和应用程序一。
这意味着可以根据您的需要使用组件的Service1
属性和Service2
函数的第二个参数在每个级别指定providers
和bootstrap
用于应用注射器。
这允许共享一组元素的依赖项实例:
所以它非常强大,您可以根据自己的需要随意组织。
以下是相应的plunkr,您可以使用它:https://plnkr.co/edit/PsySVcX6OKtD3A9TuAEw?p=preview。
Angular2文档中的这个链接可以帮助您:https://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html。
希望它可以帮助你(对不起,答案很长), 亨利
答案 1 :(得分:5)
bootstrap()
将它们放在应用程序的根目录下。 @Injectable()
装饰器。boot.ts
import {bootstrap} from 'angular2/platform/browser';
import {AppComponent} from './app.component';
import {MyFirstSvc} from '../services/MyFirstSvc';
import {MySecondSvc} from '../services/MySecondSvc';
bootstrap(AppComponent, [MyFirstSvc, MySecondSvc]);
MySecondSvc.ts
import {Injectable} from 'angular2/core';
import {MyFirstSvc} from '../services/MyFirstSvc';
@Injectable()
export class MySecondSvc {
constructor(private _firstSvc:MyFirstSvc) {}
getValue() {
return this._firstSvc.value;
}
}
有关其他文件,请参阅 Plunker 。
服务DI有点奇怪的是它依赖于组件。例如,MySecondSvc
是在组件请求时创建的,并且取决于组件树中“提供”MyFirstSvc
的位置,这可能会影响MyFirstSvc
实例注入MySecondSvc
实例}。这里将对此进行更多讨论:Can you only inject services into services through bootstrap?
答案 2 :(得分:4)
服务被视为在组件之间共享。所以,假设我有一项服务,我可以在不同的组件中使用它。
这里在这个答案中,我向您展示了一个服务,它接受来自一个组件的数据并将该数据发送到其他组件。
我使用了路由,共享服务,共享对象的概念。 我希望这有助于您了解共享服务的基本知识。
注意: @Injectable decorater用于使服务可注入。
<强> Boot.ts 强>
import {Component,bind} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
import {Router,ROUTER_PROVIDERS,RouteConfig, ROUTER_DIRECTIVES,APP_BASE_HREF,LocationStrategy,RouteParams,ROUTER_BINDINGS} from 'angular2/router';
import {SharedService} from 'src/sharedService';
import {ComponentFirst} from 'src/cone';
import {ComponentTwo} from 'src/ctwo';
@Component({
selector: 'my-app',
directives: [ROUTER_DIRECTIVES],
template: `
<h1>
Home
</h1>
<router-outlet></router-outlet>
`,
})
@RouteConfig([
{path:'/component-first', name: 'ComponentFirst', component: ComponentFirst}
{path:'/component-two', name: 'ComponentTwo', component: ComponentTwo}
])
export class AppComponent implements OnInit {
constructor(router:Router)
{
this.router=router;
}
ngOnInit() {
console.log('ngOnInit');
this.router.navigate(['/ComponentFirst']);
}
}
bootstrap(AppComponent, [SharedService,
ROUTER_PROVIDERS,bind(APP_BASE_HREF).toValue(location.pathname)
]);
<强> FirstComponent 强>
import {Component,View,bind} from 'angular2/core';
import {SharedService} from 'src/sharedService';
import {Router,ROUTER_PROVIDERS,RouteConfig, ROUTER_DIRECTIVES,APP_BASE_HREF,LocationStrategy,RouteParams,ROUTER_BINDINGS} from 'angular2/router';
@Component({
//selector: 'f',
template: `
<div><input #myVal type="text" >
<button (click)="send(myVal.value)">Send</button>
`,
})
export class ComponentFirst {
constructor(service:SharedService,router:Router){
this.service=service;
this.router=router;
}
send(str){
console.log(str);
this.service.saveData(str);
console.log('str');
this.router.navigate(['/ComponentTwo']);
}
}
<强> SecondComponent 强>
import {Component,View,bind} from 'angular2/core';
import {SharedService} from 'src/sharedService';
import {Router,ROUTER_PROVIDERS,RouteConfig, ROUTER_DIRECTIVES,APP_BASE_HREF,LocationStrategy,RouteParams,ROUTER_BINDINGS} from 'angular2/router';
@Component({
//selector: 'f',
template: `
<h1>{{myName}}</h1>
<button (click)="back()">Back<button>
`,
})
export class ComponentTwo {
constructor(router:Router,service:SharedService)
{
this.router=router;
this.service=service;
console.log('cone called');
this.myName=service.getData();
}
back()
{
console.log('Back called');
this.router.navigate(['/ComponentFirst']);
}
}
SharedService和共享对象
import {Component, Injectable,Input,Output,EventEmitter} from 'angular2/core'
// Name Service
export interface myData {
name:string;
}
@Injectable()
export class SharedService {
sharingData: myData={name:"nyks"};
saveData(str){
console.log('save data function called' + str + this.sharingData.name);
this.sharingData.name=str;
}
getData:string()
{
console.log('get data function called');
return this.sharingData.name;
}
}
答案 3 :(得分:2)
以某种方式@Injectable在Angular 2.0.0-beta.17 中连接时,对ComponentA进行连接 - &gt; ServiceB - &gt; ServiceC。
我采用了这种方法:
运行this Plunker以查看下面的示例或视图代码
app.ts
@Component({selector: 'my-app',
template: `Hello! This is my app <br/><br/><overview></overview>`,
directives: [OverviewComponent]
})
class AppComponent {}
bootstrap(AppComponent);
overview.ts
import {Component, bind} from 'angular2/core';
import {OverviewService} from "../services/overview-service";
import {PropertiesService} from "../services/properties-service";
@Component({
selector: 'overview',
template: `Overview listing here!`,
providers:[OverviewService, PropertiesService] // Include BOTH services!
})
export default class OverviewComponent {
private propertiesService : OverviewService;
constructor( overviewService: OverviewService) {
this.propertiesService = overviewService;
overviewService.logHello();
}
}
概述-service.ts
import {PropertiesService} from "./properties-service";
import {Inject} from 'angular2/core';
export class OverviewService {
private propertiesService:PropertiesService;
// Using @Inject in constructor
constructor(@Inject(PropertiesService) propertiesService:PropertiesService){
this.propertiesService = propertiesService;
}
logHello(){
console.log("hello");
this.propertiesService.logHi();
}
}
属性 - service.ts
// Using @Injectable here doesn't make a difference
export class PropertiesService {
logHi(){
console.log("hi");
}
}
答案 4 :(得分:2)
不确定是否仍然需要答案,所以我会继续尝试回答这个问题。
考虑以下示例,其中我们有一个Component,它使用服务在其模板中填充某些值,如下所示
testComponent.component.ts
import { Component } from "@angular/core"
import { DataService } from "./data.service"
@Component({
selector:"test-component",
template:`<ul>
<li *ngFor="let person of persons">{{ person.name }}</li>
</ul>
})
export class TestComponent {
persons:<Array>;
constructor(private _dataService:DataService){
this.persons = this._dataService.getPersons()
}
}
上面的代码非常简单,它将尝试获取从DataService返回的任何getPersons。 DataService文件位于下方。
data.service.ts
export class DataService {
persons:<Array>;
constructor(){
this.persons = [
{name: "Apoorv"},
{name: "Bryce"},
{name: "Steve"}
]
}
getPersons(){
return this.persons
}
如果不使用@Injectable装饰器,上面的代码将完美无缺。但是当我们的服务(在这种情况下为DataService)需要一些依赖性时,问题将开始。断点续传。如果我们更改我们的data.service.ts
文件,我们会收到错误Cannot resolve all parameters for DataService(?). Make sure they all have valid type or annotations.
import { Http } from '@angular/http';
export class DataService {
persons:<Array>;
constructor(){
this.persons = [
{name: "Apoorv"},
{name: "Bryce"},
{name: "Steve"}
]
}
getPersons(){
return this.persons
}
这与装饰器在Angular 2中的运行方式有关。请阅读https://blog.thoughtram.io/angular/2015/05/03/the-difference-between-annotations-and-decorators.html以深入了解此问题。
上面的代码也不起作用,因为我们也必须在我们的bootstrap模块中导入HTTP。
但我可以建议的一个拇指规则是,如果你的服务文件需要依赖,那么你应该用装饰器@Injectable来装饰那个类。
参考:https://blog.thoughtram.io/angular/2015/09/17/resolve-service-dependencies-in-angular-2.html
答案 5 :(得分:0)
要做的第一件事是使用@Injectable
注释注释所有服务。注意注释末尾的括号,如果没有这个,这个解决方案就无法工作。
完成后,我们可以使用构造函数注入将服务注入到彼此中:
@Injectable()
export class MyFirstSvc {
}
@Injectable()
export class MySecondSvc {
constructor(helpfulService: MyFirstSvc) {
}
}
@Injectable()
export class MyThirdSvc {
constructor(helpfulService: MyFirstSvc) {
}
}
答案 6 :(得分:0)
首先您需要提供服务
你可以在bootstrap方法中提供它:
bootstrap(AppComponent,[MyFirstSvc]);
或应用程序组件或任何其他组件,具体取决于您的需求。:
@Component({
...
providers:[MyFirstSvc]
}
...
然后使用构造函数注入您的服务:
export class MySecondSvc {
constructor(private myFirstSvc : MyFirstSvc ){}
}