Наследование и инъекция зависимостей
у меня есть набор компонентов angular2, которые должны все получить некоторую услугу. Моя первая мысль заключалась в том, что было бы лучше создать суперкласс и внедрить там сервис. Любой из моих компонентов затем расширит этот суперкласс, но этот подход не работает.
упрощенный пример:
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
}
}
я мог бы решить эту проблему путем введения MyService
внутри каждого компонента и использовать этот аргумент для super()
звонок, но это определенно какой-то абсурд.
Как правильно организовать мои компоненты, чтобы они унаследовали службу от суперкласса?
5 ответов:
я мог бы решить это, введя MyService в каждый компонент и использовать этот аргумент для вызова super (), но это определенно какой-то абсурд.
это не абсурд. Вот как работают конструкторы и инъекция конструктора.
каждый вводимый класс должен объявить зависимости в качестве параметров конструктора, и если суперкласс также имеет зависимости, они должны быть перечислены в конструкторе подкласса и переданы вместе с суперклассом с
super(dep1, dep2)
звонок.передача Вокруг инжектора и получение зависимостей императивно имеет серьезные недостатки.
он скрывает зависимости, что делает код труднее читать.
Это нарушает ожидания одного знакомого с тем, как работает Angular2 DI.
Он разбивает автономную компиляцию, которая генерирует статический код для замены декларативного и императивного DI для повышения производительности и уменьшения размера кода.
обновленное решение, предотвращает создание нескольких экземпляров myService с помощью глобального инжектора.
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(); } }
Это гарантирует, что MyService может использоваться в любом классе, который расширяет AbstractComponent без необходимости вводить MyService в каждый производный класс.
есть некоторые минусы этого решения (см. Ccomment от @Günter Zöchbauer ниже моего первоначального вопроса):
- впрыскивать глобальный инжектор только улучшение когда есть несколько различных услуг, которые должны быть введены во многих местах. Если у вас есть только одна общая служба, то, вероятно, лучше/проще внедрить эту службу в производный класс(ы)
- мое решение и предложенная им альтернатива имеют как недостаток, так и то, что они затрудняют понимание того, какой класс зависит от какой службы.
для очень хорошо написанного объяснения инъекции зависимостей в Angular2 см. Этот пост в блоге, который очень помог мне решите проблему: http://blog.thoughtram.io/angular/2015/05/18/dependency-injection-in-angular-2.html
вместо того, чтобы вводить все службы вручную, я создал класс, предоставляющий услуги, например, он получает вводимые услуги. Этот класс затем вводится в производные классы и передается в базовый класс.
производный класс:
@Component({ ... providers: [ProviderService] }) export class DerivedComponent extends BaseComponent { constructor(protected providerService: ProviderService) { super(providerService); } }
базовый класс:
export class BaseComponent { constructor(protected providerService: ProviderService) { // do something with providerService } }
класс предоставления услуг:
@Injectable() export class ProviderService { constructor(private _apiService: ApiService, private _authService: AuthService) { } }
если родительский класс был получен из стороннего плагина (и вы не можете изменить Источник), вы можете сделать это:
import { Injector } from '@angular/core'; export MyComponent extends AbstractComponent { constructor( protected injector: Injector, private anotherService: AnotherService ) { super(injector.get(MyService)); } }
или самый лучший способ (остаться только один параметр в конструктор):
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); } }
вместо того, чтобы вводить службу, которая имеет все другие службы в качестве зависимостей, например:
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 } }
Я бы пропустил этот дополнительный шаг и просто добавил inject все службы в BaseComponent, например:
class BaseComponent { constructor(protected service1: Service1, protected service2: Service2) {} } class DerivedComponent extends BaseComponent { ngOnInit() { this.service1; this.service2; } }
этот метод предполагает 2 вещи:
ваша забота полностью связана с наследованием компонентов. Скорее всего, причина, по которой вы приземлились на этот вопрос, заключается в подавляющем количестве несухих (влажных?) код нужно повторить в каждом производном классе. Если вы хотите преимущества одной точки входа для всех ваших компонентов и услуги, вам нужно будет сделать дополнительный шаг.
каждый компонент расширяет
BaseComponent
существует также недостаток, если вы решите использовать конструктор производного класса, так как вам нужно будет вызвать
super()
и передать все зависимости. Хотя я действительно не вижу прецедента, который требует использованиеconstructor
вместоngOnInit
, вполне возможно, что такой вариант использования не существует.