javascript - controladores - que es inyeccion de dependencias angular
¿Cuál es la mejor manera de inyectar un servicio en otro en angular 2(Beta)? (7)
De alguna manera, @Injectable no funciona para mí en Angular 2.0.0-beta.17 cuando se conecta el Componente A -> Servicio B -> Servicio C.
Tomé este enfoque:
- Consulte todos los servicios en el campo de proveedores de @ ComponentA.
- En ServiceB, use la anotación @Inject en el constructor para conectar ServiceC.
Ejecute este Plunker para ver un ejemplo o ver el código a continuación
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();
}
}
overview-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();
}
}
propiedades-servicio.ts
// Using @Injectable here doesn''t make a difference
export class PropertiesService {
logHi(){
console.log("hi");
}
}
Sé cómo inyectar un servicio en un componente (a través de @Component), pero ¿cómo puedo usar DI para pasar servicios fuera de los componentes?
En otras palabras, no quiero hacer esto:
export class MyFirstSvc {
}
export class MySecondSvc {
constructor() {
this.helpfulService = new MyFirstSvc();
}
}
export class MyThirdSvc {
constructor() {
this.helpfulService = new MyFirstSvc();
}
}
El servicio se considera compartido entre los componentes. Entonces, digamos que si tengo un servicio, puedo usarlo en diferentes componentes.
Aquí, en esta respuesta, le muestro un servicio que acepta datos de un componente y los envía a otro componente.
He utilizado el concepto de enrutamiento, servicio compartido, objeto compartido. Espero que esto te ayude a entender lo básico del servicio compartido.
Nota: @ Decorater inyectable se utiliza para hacer que el servicio sea inyectable.
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'']);
}
}
Segundo componente
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 y 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;
}
}
Lo primero que debe hacer es anotar todos los servicios con la anotación
@Injectable
.
Observe los paréntesis al final de la anotación, sin esto esta solución no funcionará.
Una vez hecho esto, podemos inyectar servicios entre nosotros utilizando la inyección del constructor:
@Injectable()
export class MyFirstSvc {
}
@Injectable()
export class MySecondSvc {
constructor(helpfulService: MyFirstSvc) {
}
}
@Injectable()
export class MyThirdSvc {
constructor(helpfulService: MyFirstSvc) {
}
}
No estoy seguro de si todavía se requiere una respuesta, así que seguiré adelante e intentaré responder.
Considere el siguiente ejemplo donde tenemos un Componente que usa un servicio para completar algunos valores en su plantilla como a continuación
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()
}
}
El código anterior es bastante simple e intentará obtener lo que getPersons devuelva del DataService. El archivo DataService está disponible a continuación.
data.service.ts
export class DataService {
persons:<Array>;
constructor(){
this.persons = [
{name: "Apoorv"},
{name: "Bryce"},
{name: "Steve"}
]
}
getPersons(){
return this.persons
}
El código anterior funcionará perfectamente bien sin el uso del decorador inyectable.
Pero el problema comenzará cuando nuestro servicio (DataService en este caso) requiera algunas dependencias como por ejemplo.
Http.
si cambiamos nuestro archivo
data.service.ts
como se muestra a continuación, obtendremos un error que
data.service.ts
que no se
Cannot resolve all parameters for DataService(?). Make sure they all have valid type or annotations.
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
}
Esto tiene algo que ver con la forma en que funcionan los decoradores en Angular 2. Lea https://blog.thoughtram.io/angular/2015/05/03/the-difference-between-annotations-and-decorators.html para obtener un comprensión profunda de este tema.
El código anterior tampoco funcionará, ya que también tenemos que importar HTTP en nuestro módulo de arranque.
Pero una regla general que puedo sugerir es que si su archivo de servicio necesita una dependencia, entonces debe decorar esa clase con un decorador @Injectable.
referencia: https://blog.thoughtram.io/angular/2015/09/17/resolve-service-dependencies-in-angular-2.html
Primero necesitas brindar tu servicio
Puede proporcionarlo en el método bootstrap:
bootstrap(AppComponent,[MyFirstSvc]);
o en el componente de la aplicación, o en cualquier otro componente, según sus necesidades .:
@Component({
...
providers:[MyFirstSvc]
}
...
entonces solo inyecta tu servicio usando el constructor:
export class MySecondSvc {
constructor(private myFirstSvc : MyFirstSvc ){}
}
Sí, lo primero es agregar el decorador
@Injectable
en cada servicio que desee inyectar.
De hecho, el nombre
Injectable
es un poco insidioso.
No significa que la clase será "inyectable", pero se decorará para poder inyectar los parámetros del constructor.
Vea este número de github para más detalles:
https://github.com/angular/angular/issues/4404
.
Aquí está mi comprensión del mecanismo de inyección.
Al configurar un decorador
@Injectable
para una clase, Angular intentará crear u obtener instancias para los tipos correspondientes en el inyector para la cadena de ejecución actual.
De hecho, no solo hay un inyector para una aplicación Angular2 sino un árbol de inyectores.
Están implícitamente asociados a toda la aplicación y componentes.
Una característica clave en este nivel es que están vinculados entre sí de forma jerárquica.
Este árbol de inyectores mapea el árbol de componentes.
No se definen inyectores para "servicios".
Tomemos una muestra. Tengo la siguiente aplicación:
-
Componente
AppComponent
: el componente principal de mi aplicación que se proporciona al crear la aplicación Angular2 en la funciónbootstrap
@Component({ selector: ''my-app'', template: ` <child></child> `, (...) directives: [ ChildComponent ] }) export class AppComponent { }
-
Componente
ChildComponent
: un subcomponente que se usará dentro del componenteAppComponent
@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; } }
-
Dos servicios,
Service1
yService2
:Service1
es utilizado porChildComponent
yChildComponent
porService1
@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'' } ]; } }
Aquí hay una descripción general de todos estos elementos y sus relaciones:
Application
|
AppComponent
|
ChildComponent
getData() --- Service1 --- Service2
En dicha aplicación, tenemos tres inyectores:
-
El inyector de la aplicación que se puede configurar con el segundo parámetro de la función
bootstrap
-
El inyector de
AppComponent
que se puede configurar utilizando el atributo deproviders
de este componente. Puede "ver" elementos definidos en el inyector de la aplicación. Esto significa que si no se encuentra un proveedor en este proveedor, se buscará automáticamente en este inyector principal. Si no se encuentra en este último, se generará un error "proveedor no encontrado". -
El inyector
ChildComponent
que seguirá las mismas reglas que elAppComponent
. Para inyectar elementos involucrados en la cadena de inyección ejecutada para el componente, se buscarán proveedores primero en este inyector, luego en elAppComponent
y finalmente en el de aplicación.
Esto significa que cuando intente inyectar el
Service1
en el constructor
ChildComponent
, Angular2 buscará en el inyector
ChildComponent
, luego en el
AppComponent
y finalmente en el de la aplicación.
Dado que
Service2
debe inyectarse en
Service1
, se realizará el mismo procesamiento de resolución: inyector
AppComponent
,
AppComponent
one y application one.
Esto significa que tanto
Service1
como
Service2
pueden especificarse en cada nivel de acuerdo con sus necesidades utilizando el atributo de
providers
para componentes y el segundo parámetro de la función
bootstrap
para el inyector de la aplicación.
Esto permite compartir instancias de dependencias para un conjunto de elementos:
- Si define un proveedor a nivel de aplicación, la instancia creada correspondiente será compartida por toda la aplicación (todos los componentes, todos los servicios, ...).
- Si define un proveedor a nivel de componente, la instancia será compartida por el componente mismo, sus subcomponentes y todos los "servicios" involucrados en la cadena de dependencia.
Por lo tanto, es muy poderoso y puede organizarse libremente como desee y para sus necesidades.
Aquí está el plunkr correspondiente para que pueda jugar con él: https://plnkr.co/edit/PsySVcX6OKtD3A9TuAEw?p=preview .
Este enlace de la documentación de Angular2 podría ayudarlo: https://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html .
Espero que te ayude (y disculpa la respuesta larga), Thierry
-
"Proporcione" sus servicios en algún lugar en el que desee utilizarlos, por ejemplo, puede colocarlos en la raíz de su aplicación utilizando
bootstrap()
si solo tiene una instancia de cada servicio (singletons). -
Use el decorador
@Injectable()
en cualquier servicio que dependa de otro. - Inyecte los otros servicios en el constructor del servicio dependiente.
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;
}
}
Ver Plunker para otros archivos.
Lo que es un poco extraño sobre Service DI es que todavía depende de los componentes.
Por ejemplo,
MySecondSvc
se crea cuando un componente lo solicita y, dependiendo de dónde se "proporcionó"
MyFirstSvc
en el árbol de componentes, eso puede afectar qué instancia de
MyFirstSvc
se inyecta en
MySecondSvc
.
Esto se discute más aquí:
¿solo puede inyectar servicios en los servicios a través de bootstrap?