inheritance - Inyección de herencia y dependencia
angular typescript (6)
Podría resolver esto inyectando MyService dentro de todos y cada uno de los componentes y usar ese argumento para la llamada super (), pero eso es definitivamente algo absurdo.
No es absurdo Así es como funcionan los constructores y la inyección de constructores.
Cada clase inyectable tiene que declarar las dependencias como parámetros del constructor y si la superclase también tiene dependencias, también deben incluirse en el constructor de la subclase y pasar a la superclase con la
super(dep1, dep2)
.
Pasar por un inyector y adquirir dependencias imperativamente tiene serias desventajas.
Oculta las dependencias, lo que hace que el código sea más difícil de leer.
Viola las expectativas de alguien familiarizado con el funcionamiento de Angular2 DI.
Rompe la compilación fuera de línea que genera código estático para reemplazar la DI declarativa e imperativa para mejorar el rendimiento y reducir el tamaño del código.
Tengo un conjunto de componentes angular2 que deberían inyectarse a todos. Mi primer pensamiento fue que sería mejor crear una superclase e inyectar el servicio allí. Cualquiera de mis componentes ampliaría esa superclase, pero este enfoque no funciona.
Ejemplo simplificado:
export class AbstractComponent {
constructor(private myservice: MyService) {
// Inject the service I need for all components
}
}
export MyComponent extends AbstractComponent {
constructor(private anotherService: AnotherService) {
super(); // This gives an error as super constructor needs an argument
}
}
Podría resolver esto inyectando
MyService
dentro de todos y cada uno de los componentes y usar ese argumento para la llamada
super()
pero eso es definitivamente algo absurdo.
¿Cómo organizar mis componentes correctamente para que hereden un servicio de la superclase?
En lugar de inyectar todos los servicios manualmente, creé una clase que proporciona los servicios, por ejemplo, obtiene los servicios inyectados. Esta clase se inyecta en las clases derivadas y se pasa a la clase base.
Clase derivada:
@Component({
...
providers: [ProviderService]
})
export class DerivedComponent extends BaseComponent {
constructor(protected providerService: ProviderService) {
super(providerService);
}
}
Clase base:
export class BaseComponent {
constructor(protected providerService: ProviderService) {
// do something with providerService
}
}
Clase de prestación de servicios:
@Injectable()
export class ProviderService {
constructor(private _apiService: ApiService, private _authService: AuthService) {
}
}
En lugar de inyectar un servicio que tiene todos los demás servicios como dependencias, así:
class ProviderService {
constructor(private service1: Service1, private service2: Service2) {}
}
class BaseComponent {
constructor(protected providerService: ProviderService) {}
ngOnInit() {
// Access to all application services with providerService
this.providerService.service1
}
}
class DerivedComponent extends BaseComponent {
ngOnInit() {
// Access to all application services with providerService
this.providerService.service1
}
}
Me saltaría este paso adicional y simplemente agregaría inyectar todos los servicios en el Componente Base, así:
class BaseComponent {
constructor(protected service1: Service1, protected service2: Service2) {}
}
class DerivedComponent extends BaseComponent {
ngOnInit() {
this.service1;
this.service2;
}
}
Esta técnica supone 2 cosas:
-
Su preocupación está completamente relacionada con la herencia de componentes. Lo más probable es que la razón por la que aterrizó en esta pregunta se deba a la abrumadora cantidad de código no seco (WET?) Que debe repetir en cada clase derivada. Si desea beneficiarse de un único punto de entrada para todos sus componentes y servicios , deberá realizar el paso adicional.
-
Cada componente extiende el
BaseComponent
También hay una desventaja si decide usar el constructor de una clase derivada, ya que necesitará llamar a
super()
y pasar todas las dependencias.
Aunque realmente no veo un caso de uso que requiera el uso del
constructor
lugar de
ngOnInit
, es completamente posible que exista dicho caso de uso.
Por lo que entiendo para heredar de la clase base, primero debe crear una instancia. Para crear una instancia, debe pasar los parámetros requeridos por su constructor, por lo tanto, pasarlos de secundario a primario a través de una llamada super () para que tenga sentido. El inyector, por supuesto, es otra solución viable.
Si la clase principal se obtuvo de un complemento de terceros (y no puede cambiar la fuente), puede hacer esto:
import { Injector } from ''@angular/core'';
export MyComponent extends AbstractComponent {
constructor(
protected injector: Injector,
private anotherService: AnotherService
) {
super(injector.get(MyService));
}
}
o la mejor manera (permanezca solo un parámetro en el constructor):
import { Injector } from ''@angular/core'';
export MyComponent extends AbstractComponent {
private anotherService: AnotherService;
constructor(
protected injector: Injector
) {
super(injector.get(MyService));
this.anotherService = injector.get(AnotherService);
}
}
Solución actualizada, evita que se generen múltiples instancias de myService utilizando el inyector global.
import {Injector} from ''@angular/core'';
import {MyServiceA} from ''./myServiceA'';
import {MyServiceB} from ''./myServiceB'';
import {MyServiceC} from ''./myServiceC'';
export class AbstractComponent {
protected myServiceA:MyServiceA;
protected myServiceB:MyServiceB;
protected myServiceC:MyServiceC;
constructor(injector: Injector) {
this.settingsServiceA = injector.get(MyServiceA);
this.settingsServiceB = injector.get(MyServiceB);
this.settingsServiceB = injector.get(MyServiceC);
}
}
export MyComponent extends AbstractComponent {
constructor(
private anotherService: AnotherService,
injector: Injector
) {
super(injector);
this.myServiceA.JustCallSomeMethod();
this.myServiceB.JustCallAnotherMethod();
this.myServiceC.JustOneMoreMethod();
}
}
Esto asegurará que MyService se pueda usar dentro de cualquier clase que extienda AbstractComponent sin la necesidad de inyectar MyService en cada clase derivada.
Hay algunas desventajas para esta solución (vea Ccomment de @ Günter Zöchbauer debajo de mi pregunta original):
- Inyectar el inyector global es solo una mejora cuando hay varios servicios diferentes que deben inyectarse en muchos lugares. Si solo tiene un servicio compartido, entonces probablemente sea mejor / más fácil inyectar ese servicio dentro de las clases derivadas
- Mi solución y su alternativa propuesta tienen la desventaja de que hacen que sea más difícil ver qué clase depende de qué servicio.
Para una explicación muy bien escrita de la inyección de dependencia en Angular2, vea esta publicación de blog que me ayudó mucho a resolver el problema: http://blog.thoughtram.io/angular/2015/05/18/dependency-injection-in-angular-2.html