providers providedin injector injectiontoken injection injectable example dependency dependency-injection typescript angular

dependency-injection - providedin - injector angular 4



Vincular una clase a una interfaz (2)

Usando mecanografiado, puedo unir fácilmente las clases a ellos mismos:

bootstrap(MyAppComponent, [MyClass]);

Sin embargo, me gustaría vincular mi clase a una interfaz, como por ejemplo:

boostrap(MyAppComponent, [???]);

tal que puedo inyectarlo de la siguiente manera:

class MyAppComponent { constructor(my_class : IMyClass){ } };

¿Es esto posible en Angular2? En caso afirmativo, ¿cómo tengo que especificar el enlace?


No sé si es posible con la interfaz ya que la interfaz no estará disponible en tiempo de ejecución (javascript no conoce la interfaz). Pero se puede hacer usando clases abstractas.

//abstract-parent-service.ts

export class DatabaseService{ getService: ()=>string; }

//hibernate.service.ts

import {DatabaseService} from "./abstract-parent-service"; export class HibernateService implements DatabaseService{ constructor() { } getService() { return "i am hibernate"; } }

//jdbc.service.ts

import {DatabaseService} from "./abstract-parent-service"; export class JDBCService implements DatabaseService{ constructor() { } getService() { return "i am Jdbc"; } }

//cmp-a.component.ts

import {DatabaseService} from "./abstract-parent-service"; import {HibernateService} from "./hibernate.service"; @Component({ selector: ''cmp-a'', template: `<h1>Hello Hibernate</h1>`, providers: [{provide: DatabaseService, useClass: HibernateService}] }) export class CmpAComponent { constructor (private databaseService: DatabaseService) { console.log("Database implementation in CompA :"+this.databaseService.getService()); } }

//cmp-b.component.ts

import {DatabaseService} from "./abstract-parent-service"; import {HibernateService} from "./hibernate.service"; @Component({ selector: ''cmp-b'', template: `<h1>Hello Jdbc</h1>`, providers: [{provide: DatabaseService, useClass: JDBCService}] }) export class CmpAComponent { constructor (private databaseService: DatabaseService) { console.log("Database implementation in CompA :"+this.databaseService.getService()); } }

Pero el problema con esta implementación es que HibernateService y JDBCService no pueden extender ninguna otra clase ahora porque ya se han casado con DatabaseService.

class A{ constructor(){ console.log("in A"); } } class B extends A{ constructor(){ super(); console.log("in B"); } } class C extends A{ constructor(){ super(); console.log("in C"); } } let c = new C(); //This thing is not possible in typescript class D extends B, C{//error: Classes can only extend a single class constructor(){ super();// which constructor B or C console.log("in D"); } }

Si está utilizando este patrón para DI, asegúrese de que los servicios de su clase secundaria no van a extender ninguna otra funcionalidad en el futuro.


Para abreviar, el problema es que las interfaces desaparecen cuando se compila el mecanografiado. Así que tendrías que usar @Inject con una cadena.

O hay otra opción, si revisa el último artículo de Victor Savkin , puede encontrar esto en los comentarios:

Algunos antecedentes En TypeScript, las interfaces son estructurales y no se conservan en tiempo de ejecución. Así que tienes que usar ILoginService de la siguiente manera:

constructor(@Inject("ILoginService") s:ILoginService).

No tienes que usar una cadena, cualquier objeto se puede pasar allí. En realidad proporcionamos un objeto llamado OpaqueToken que se puede utilizar para este propósito.

interface ILoginService { login(credentials);} const ILoginService = new OpaqueToken("LoginService");

se puede utilizar de esta manera:

constructor(@Inject(ILoginService) s:ILoginService).