Javascript 在 angular 2 (Beta) 中将一项服务注入另一项服务的最佳方法是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/34804298/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What's the best way to inject one service into another in angular 2 (Beta)?
提问by Bryce Johnson
I know how to inject a service into a component (via @Component), but how can I use DI to pass around services outside of components?
我知道如何将服务注入到组件中(通过 @Component),但是我如何使用 DI 来传递组件之外的服务?
In other words, I don't want to do this:
换句话说,我不想这样做:
export class MyFirstSvc {
}
export class MySecondSvc {
constructor() {
this.helpfulService = new MyFirstSvc();
}
}
export class MyThirdSvc {
constructor() {
this.helpfulService = new MyFirstSvc();
}
}
回答by Thierry Templier
Yes, the first thing is to add the @Injectable
decorator on each services you want to inject. In fact, the Injectable
name is a bit insidious. It doesn't mean that the class will be "injectable" but it will decorate so the constructor parameters can be injected. See this github issue for more details: https://github.com/angular/angular/issues/4404.
是的,第一件事是@Injectable
在要注入的每个服务上添加装饰器。其实这个Injectable
名字有点阴险。这并不意味着该类将是“可注入的”,但它会进行装饰,以便可以注入构造函数参数。有关更多详细信息,请参阅此 github 问题:https: //github.com/angular/angular/issues/4404。
Here is my understanding of the injection mechanism. When setting an @Injectable
decorator for a class, Angular will try to create or get instances for corresponding types in the injector for the current execution chain. In fact, there is not only one injector for an Angular2 application but a tree of injectors. They are implicitly associated to the whole application and components. One key feature at this level is that they are linked together in a hierarchical way. This tree of injectors maps the tree of components. No injectors are defined for "services".
以上是我对注入机制的理解。@Injectable
为类设置装饰器时,Angular 将尝试为当前执行链的注入器中的相应类型创建或获取实例。事实上,Angular2 应用程序不仅有一个注入器,而且有一个注入器树。它们与整个应用程序和组件隐式关联。此级别的一个关键功能是它们以分层方式链接在一起。这个注入器树映射了组件树。没有为“服务”定义注入器。
Let's take a sample. I have the following application:
让我们举个例子。我有以下应用程序:
Component
AppComponent
: the main component of my application that is provided when creating the Angular2 application in thebootstrap
function@Component({ selector: 'my-app', template: ` <child></child> `, (...) directives: [ ChildComponent ] }) export class AppComponent { }
Component
ChildComponent
: a sub component that will be used within theAppComponent
component@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; } }
Two services,
Service1
andService2
:Service1
is used by theChildComponent
andService2
byService1
@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' } ]; } }
组件
AppComponent
:在bootstrap
函数中创建Angular2应用程序时提供的我的应用程序的主要组件@Component({ selector: 'my-app', template: ` <child></child> `, (...) directives: [ ChildComponent ] }) export class AppComponent { }
Component
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' } ]; } }
Here is an overview of all these elements and there relations:
以下是所有这些元素及其关系的概述:
Application
|
AppComponent
|
ChildComponent
getData() --- Service1 --- Service2
In such application, we have three injectors:
在这样的应用中,我们有三个注入器:
- The application injector that can be configured using the second parameter of the
bootstrap
function - The
AppComponent
injector that can be configured using theproviders
attribute of this component. It can "see" elements defined in the application injector. This means if a provider isn't found in this provider, it will be automatically look for into this parent injector. If not found in the latter, a "provider not found" error will be thrown. - The
ChildComponent
injector that will follow the same rules than theAppComponent
one. To inject elements involved in the injection chain executed forr the component, providers will be looked for first in this injector, then in theAppComponent
one and finally in the application one.
- 可以使用
bootstrap
函数的第二个参数配置的应用程序注入器 AppComponent
可以使用providers
该组件的属性配置的注入器。它可以“看到”应用程序注入器中定义的元素。这意味着如果在此提供程序中找不到提供程序,它将自动在此父注入器中查找。如果在后者中没有找到,则会抛出“provider not found”错误。ChildComponent
将遵循相同规则的注入器AppComponent
。为了注入为组件执行的注入链中涉及的元素,将首先在此注入器中查找提供者,然后在注入器中查找AppComponent
,最后在应用程序中查找。
This means that when trying to inject the Service1
into the ChildComponent
constructor, Angular2 will look into the ChildComponent
injector, then into the AppComponent
one and finally into the application one.
这意味着当尝试将 注入Service1
到ChildComponent
构造函数中时,Angular2 将查看注入ChildComponent
器,然后查看注入器,AppComponent
最后查看应用程序。
Since Service2
needs to be injected into Service1
, the same resolution processing will be done: ChildComponent
injector, AppComponent
one and application one.
由于Service2
需要注入到 中Service1
,所以会做同样的解析处理:ChildComponent
injector, AppComponent
one 和 application one。
This means that both Service1
and Service2
can be specified at each level according to your needs using the providers
attribute for components and the second parameter of the bootstrap
function for the application injector.
这意味着,Service1
和Service2
可以在每个级别根据使用您的需求指定providers
为组件属性和的第二参数bootstrap
为应用注射器功能。
This allows to share instances of dependencies for a set of elements:
这允许共享一组元素的依赖项实例:
- If you define a provider at the application level, the correspoding created instance will be shared by the whole application (all components, all services, ...).
- If you define a provider at a component level, the instance will be shared by the component itself, its sub components and all the "services" involved in the dependency chain.
- 如果您在应用程序级别定义提供者,则相应的创建实例将被整个应用程序(所有组件、所有服务……)共享。
- 如果在组件级别定义提供者,则该实例将由组件本身、其子组件以及依赖链中涉及的所有“服务”共享。
So it's very powerful and you're free to organize as you want and for your needs.
所以它非常强大,您可以根据自己的需要自由组织。
Here is the corresponding plunkr so you can play with it: https://plnkr.co/edit/PsySVcX6OKtD3A9TuAEw?p=preview.
这是相应的 plunkr,因此您可以使用它:https://plnkr.co/edit/PsySVcX6OKtD3A9TuAEw?p=preview。
This link from the Angular2 documentation could help you: https://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html.
Angular2 文档中的此链接可以帮助您:https://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html 。
Hope it helps you (and sorry the long answer), Thierry
希望它对你有帮助(对不起,答案太长),蒂埃里
回答by Mark Rajcok
- "Provide" your services somewhere at or above where you intend to use them, e.g., you could put them at the root of your application using
bootstrap()
if you only one once instance of each service (singletons). - Use the
@Injectable()
decorator on any service that depends on another. - Inject the other services into the constructor of the dependent service.
- 在您打算使用它们的地方或上方“提供”您的服务,例如,
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
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;
}
}
See Plunkerfor other files.
Plunker其他文件请参见。
What's a bit odd about Service DI is that it still depends on components. E.g., MySecondSvc
is created when a component requests it, and depending on where MyFirstSvc
was "provided" in the component tree, that can affect which MyFirstSvc
instance is injected into MySecondSvc
. This is discussed more here: Can you only inject services into services through bootstrap?
Service DI 有点奇怪的是它仍然依赖于组件。例如, MySecondSvc
在组件请求它时创建,并且取决于MyFirstSvc
组件树中“提供”的位置,这可能会影响将哪个MyFirstSvc
实例注入到MySecondSvc
. 此处讨论更多:您能否仅通过引导程序将服务注入服务?
回答by micronyks
Service is considered to be shared among components. So let's say if I have one service, I can use it in different components.
服务被认为是在组件之间共享的。所以假设我有一项服务,我可以在不同的组件中使用它。
Here In this answer I'm showing you one service which accepts data from one component and sends that data to other component.
在这个答案中,我向您展示了一种服务,它接受来自一个组件的数据并将该数据发送到另一个组件。
I have used concept of Routing, Shared-Service, Shared-Object. I hope this will help you to understand the basic of share-service.
我使用了路由、共享服务、共享对象的概念。我希望这能帮助您了解共享服务的基础知识。
Note: @Injectabledecorater is used to make service injectable.
注意:@Injectable装饰器用于使服务可注入。
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 and shared Object
共享服务和共享对象
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;
}
}
回答by Julius
Somehow @Injectable doesn't work for me in Angular 2.0.0-beta.17when wiring ComponentA -> ServiceB -> ServiceC.
不知何故,@Injectable 在 Angular 2.0.0-beta.17 中连接 ComponentA -> ServiceB -> ServiceC 时对我不起作用。
I took this approach:
我采取了这种方法:
- Reference all services in the @ComponentA's providers field.
- In ServiceB use the @Inject annotation in the constructor to wire ServiceC.
- 引用@ComponentA 的提供者字段中的所有服务。
- 在 ServiceB 中,在构造函数中使用 @Inject 注释来连接 ServiceC。
Run this Plunkerto see an example or view code below
运行此 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
概览.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();
}
}
overview-service.ts
概览-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();
}
}
properties-service.ts
属性-service.ts
// Using @Injectable here doesn't make a difference
export class PropertiesService {
logHi(){
console.log("hi");
}
}
回答by Apoorv
not sure if an answer is still required so i would go ahead and try to answer this.
不确定是否仍然需要答案,所以我会继续尝试回答这个问题。
Consider the following example where we have a Component which uses a service to populate some values in its template like below
考虑以下示例,其中我们有一个组件,它使用服务在其模板中填充一些值,如下所示
testComponent.component.ts
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()
}
}
The above code is pretty simple and it will try to fetch whatever getPersons return from the DataService. The DataService file is available below.
上面的代码非常简单,它将尝试从 DataService 中获取 getPersons 返回的任何内容。DataService 文件在下面可用。
data.service.ts
data.service.ts
export class DataService {
persons:<Array>;
constructor(){
this.persons = [
{name: "Apoorv"},
{name: "Bryce"},
{name: "Steve"}
]
}
getPersons(){
return this.persons
}
The above piece of code will work perfectly fine without the use of the @Injectable decorator. But the problem will start when our service(DataService in this case) requires some dependencies like for eg. Http. if we change our data.service.ts
file as below we will get an error saying Cannot resolve all parameters for DataService(?). Make sure they all have valid type or annotations.
上面的代码可以在不使用 @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
}
This has something to do with the way decorators function in Angular 2. Please read https://blog.thoughtram.io/angular/2015/05/03/the-difference-between-annotations-and-decorators.htmlto get an in depth understanding of this issue.
这与 Angular 2 中装饰器的工作方式有关。请阅读https://blog.thoughtram.io/angular/2015/05/03/the-difference-between-annotations-and-decorators.html以获取深入了解这个问题。
The above code will also not work as we have to import HTTP in our bootstrap module as well.
上面的代码也不起作用,因为我们也必须在引导模块中导入 HTTP。
But a thumb rule i can suggest is that if your service file needs a dependency then you should decorate that class with a decorator @Injectable.
但是我可以建议的一个经验法则是,如果您的服务文件需要依赖项,那么您应该使用装饰器@Injectable 来装饰该类。
reference:https://blog.thoughtram.io/angular/2015/09/17/resolve-service-dependencies-in-angular-2.html
参考:https: //blog.thoughtram.io/angular/2015/09/17/resolve-service-dependencies-in-angular-2.html
回答by bougsid
First You need to provide your service
首先您需要提供您的服务
You could provide it either in the bootstrap method:
您可以在 bootstrap 方法中提供它:
bootstrap(AppComponent,[MyFirstSvc]);
or the on the app component, or in any other component, depending on your needs.:
或在应用程序组件上,或在任何其他组件中,具体取决于您的需要:
@Component({
...
providers:[MyFirstSvc]
}
...
then just inject you service using the constructor :
然后只需使用构造函数注入您的服务:
export class MySecondSvc {
constructor(private myFirstSvc : MyFirstSvc ){}
}
回答by Angular University
The first thing to do is to annotate all services with the @Injectable
annotation. Notice the parentheses at the end of the annotation, without this this solution won't work.
首先要做的是用注解来注解所有服务@Injectable
。请注意注释末尾的括号,否则此解决方案将不起作用。
Once this is done, we can then inject services into each other using constructor injection:
完成后,我们可以使用构造函数注入将服务注入彼此:
@Injectable()
export class MyFirstSvc {
}
@Injectable()
export class MySecondSvc {
constructor(helpfulService: MyFirstSvc) {
}
}
@Injectable()
export class MyThirdSvc {
constructor(helpfulService: MyFirstSvc) {
}
}