Как расширить компонент с помощью внедрения зависимостей?

У меня есть следующий класс ModuleWithHttp:

@Injectable()
export default class {
  constructor(private fetchApi: FetchApi) {}
}

и я хочу использовать его следующим образом:

@Component({
  selector: 'main',
  providers: [FetchApi]
})
export default class extends ModuleWithHttp {
  onInit() {
    this.fetchApi.call();
  }
}

Поэтому, расширяя суперкласс, который уже внедряет зависимость, я хочу иметь доступ к нему в его дочерних элементах.

Я пробовал много разных способов, даже имея супер-класс в качестве компонента:

@Component({
  providers: [FetchApi]
})
export default class {
  constructor(private fetchApi: FetchApi) {}
}

Но все же this.fetchApi является null, даже в суперклассе.

Ответы

Ответ 1

Вы должны ввести fetchAPI в суперкласс и передать его дочернему классу

export default class extends ModuleWithHttp {

  constructor(fetchApi: FetchApi) {
     super(fetchApi);
  }   

  onInit() {
    this.fetchApi.call();
  }
}

Это особенность работы DI в целом. Суперкласс будет инстанцировать ребенка через наследование, но вы должны предоставить необходимые параметры ребенку.

Ответ 2

Если вы хотите избежать использования кода "котельной пластины" для инъекций в дочерних классах только для инъекций в конструкторе родительских классов, а затем эффективно использовать эти сервисы в дочерних классах через наследование, вы можете сделать это:

edit: от Angular 5.0.0 ReflectiveInjector устарел в пользу StaticInjector, ниже обновлен код, чтобы отразить это изменение

У вас есть карта услуг с папками,

export const services: {[key: string]: {provide: any, deps: any[], useClass?: any}} = {
  'FetchApi': {
    provide: FetchApi,
    deps: []
  }
}

Держите держатель инжектора,

import {Injector} from "@angular/core";

export class ServiceLocator {
  static injector: Injector;
}

настройте его в AppModule,

@NgModule(...)
export class AppModule {
  constructor() {
    ServiceLocator.injector = Injector.create(
      Object.keys(services).map(key => ({
        provide: services[key].provide,
        useClass: services[key].provide,
        deps: services[key].deps
      }))
    );
  }
}

используйте инжектор в родительском классе,

export class ParentClass {

  protected fetchApi: FetchApi;

  constructor() {
    this.fetchApi = ServiceLocator.injector.get(FetchApi);
    ....
  }
}

и расширьте родительский класс, поэтому вам не нужно вводить службу FetchApi.

export class ChildClass extends ParentClass {
  constructor() {
    super();
    ...
  }

  onInit() {
    this.fetchApi.call();
  }
}

Ответ 3

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

Мой базовый класс:

constructor(private injectorObj: Injector) {

  this.store = <Store<AppState>>this.injectorObj.get(Store);
  this.apiService = this.injectorObj.get(JsonApiService);
  this.dialogService = this.injectorObj.get(DialogService);
  this.viewContainerRef = this.injectorObj.get(ViewContainerRef);
  this.router = this.injectorObj.get(Router);
  this.translate = this.injectorObj.get(TranslateService);
  this.globalConstantsService = this.injectorObj.get(GlobalConstantsService);
  this.dialog = this.injectorObj.get(MatDialog);
  this.activatedRoute = this.injectorObj.get(ActivatedRoute);
}

Мои детские классы:

constructor( private injector: Injector) {

   super(injector);

}

Ответ 4

Вы можете внедрить любой сервис или класс из родительского класса с помощью класса Injector, вам нужно внедрить класс Injector из дочернего объекта и передать его родителю с помощью super(injector) чтобы родитель мог внедрить ваши повторно используемые услуги из инжектора, исходящего от дочернего элемента.

Родительский класс:

export class BaseComponent implements OnInit {

    protected myService: MyService

    constructor(injector: Injector) {
        this.myService = injector.get(MyService)
    }

    ngOnInit() {
        console.log('ngOnInit from Base');
    }
}

Детский класс:

export class AppComponent extends BaseComponent {

  constructor(injector: Injector) {
    super(injector)
  }

  ngOnInit() {
    super.ngOnInit()
    this.myService.getUsers()
  }
}

Используя этот способ, вы не хотите внедрять каждую службу от дочерней, чтобы передавать одну за другой родителю, вышеуказанный способ более эффективен для внедрения от родительской.

форсунка

import { Component, Injector } from '@angular/core';