Подтвердить что ты не робот

Наследование и зависимость

У меня есть набор 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(), но это определенно какой-то абсурд.

Как правильно упорядочить мои компоненты так, чтобы они наследовали сервис из суперкласса?

4b9b3361

Ответ 1

Обновленное решение, предотвращает создание нескольких экземпляров 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

Ответ 2

Я мог бы решить эту проблему, внедрив MyService в каждый компонент и использовать этот аргумент для вызова super(), но это определенно какой-то абсурд.

Это не абсурдно. Так работает конструктор и инжекция конструктора.

Каждый инъецируемый класс должен объявлять зависимости как параметры конструктора, и если у суперкласса также есть зависимости, они также должны быть перечислены в конструкторе подкласса и переданы в суперкласс с super(dep1, dep2).

Обход инжектора и получение зависимостей обязательно имеет серьезные недостатки.

Он скрывает зависимости, которые затрудняют чтение кода.
Это противоречит ожиданиям тех, кто знаком с тем, как работает Angular2 DI.
Это нарушает автономную компиляцию, которая генерирует статический код, чтобы заменить декларативный и императивный DI для повышения производительности и уменьшения размера кода.

Ответ 3

Вместо того, чтобы вводить все службы вручную, я создал класс, предоставляющий услуги, например, он получает введенные услуги. Затем этот класс вводится в производные классы и передается базовому классу.

Производный класс:

@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) {
    }
}

Ответ 4

Вместо внедрения службы, которая имеет все другие службы в качестве зависимостей, вот так:

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
    }
}

Я бы пропустил этот дополнительный шаг и просто добавил бы инжект всех сервисов в BaseComponent, вот так:

class BaseComponent {
    constructor(protected service1: Service1, protected service2: Service2) {}
}

class DerivedComponent extends BaseComponent {
    ngOnInit() {
        this.service1;
        this.service2;
    }
}

Эта техника предполагает 2 вещи:

  1. Ваша забота полностью связана с наследованием компонентов. Скорее всего, причина, по которой вы попали на этот вопрос, заключается в том, что огромное количество несухого (WET?) Кода необходимо повторять в каждом производном классе. Если вы хотите воспользоваться преимуществами единой точки входа для всех ваших компонентов и услуг, вам потребуется выполнить дополнительный шаг.

  2. Каждый компонент расширяет BaseComponent

Есть также недостаток, если вы решите использовать конструктор производного класса, так как вам нужно будет вызвать super() и передать все зависимости. Хотя я на самом деле не вижу ngOnInit использования, который требует использования constructor вместо ngOnInit, вполне возможно, что такой вариант использования существует.

Ответ 5

Если родительский класс был получен из стороннего плагина (и вы не можете изменить источник), вы можете сделать это:

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);
  }
}