Angular2 и поддержка наследования классов

Я создаю приложение Angular2 в Typescript и хотел бы использовать функциональность системы класса (читать: наследование класса), предлагаемую Typescript. Тем не менее, кажется, что Angular2 не играет хорошо с производными классами. Я ищу некоторую помощь в работе моего приложения.

Проблема, с которой я сталкиваюсь, заключается в том, что у меня есть базовый класс и выведите несколько дочерних классов. Когда я создаю свое дерево компонентов, я хотел бы иметь доступ к родительским/дочерним компонентам компонентов (в любом случае это хорошо). Насколько я понимаю, Angular2 предлагает два варианта:

  • Вставить родительский элемент в дочерний компонент
  • Используйте ContentChildren (или ViewChildren) для доступа к дочерним элементам компонента.

Оба работают нормально, если вы знаете тип класса, с которым работаете (ChildComponent), но, похоже, не работают, когда пытаетесь использовать базовый класс этих компонентов (BaseComponent) в качестве селектора.

Чтобы визуализировать его в некотором коде (см. этот Plunker для живой демонстрации), у меня есть компонент/класс приложения следующим образом:

@Component({
  selector: 'my-app',
  template: `<parent-comp>
      <child-comp1></child-comp1>
      <child-comp1></child-comp1>
      <child-comp2></child-comp2>
    </parent-comp>`,
  directives: [ParentComponent, ChildComponent1, ChildComponent2]
})
export class MyApplication  {
}

Базовый класс и дочерние классы определяются как:

export class BaseComponent {
  // Interesting stuff here
}

@Component({
  selector: 'child-comp2',
  template: '<div>child component #2</div>'
})
export class ChildComponent2 extends BaseComponent {
}

@Component({
  selector: 'child-comp1',
  template: '<div>child component #1</div>'
})
export class ChildComponent1 extends BaseComponent {
}

И у родительского класса есть своя логика для подсчета своих детей.

@Component({
  selector: 'parent-comp',
  template: `<div>Hello World</div>
   <p>Number of Child Component 1 items: {{numComp1}}
   <p>Number of Child Component 2 items: {{numComp2}}
   <p>Number of Base Component items: {{numBase}}
   <p><ng-content></ng-content>
  `
})
export class ParentComponent implements AfterContentChecked  {

  @ContentChildren(ChildComponent1) contentChild1: QueryList<ChildComponent1>
  @ContentChildren(ChildComponent2) contentChild2: QueryList<ChildComponent2>
  @ContentChildren(BaseComponent) contentBase: QueryList<BaseComponent>
  public numComp1:number
  public numComp2:number
  public numBase:number

  ngAfterContentChecked() {
    this.numComp1 = this.contentChild1.length
    this.numComp2 = this.contentChild2.length
    this.numBase = this.contentBase.length
  }

(Опять же, вы можете увидеть живое демо здесь)

Вывод для первых двух счетчиков будет таким, как ожидалось. Есть 2 ChildComponent1 и 1 ChildComponent2 дети. К сожалению, счетчик BaseComponent не отображает сумму этих счетчиков, но показывает 0. Он не находит в дочерних классах класс BaseComponent.

То же самое происходит, когда ParentComponent также распространяется из BaseComponent, и вы хотите вложить его в ChildComponent. Инжектор потребует определенного типа ParentComponent и не может работать с базовым классом.

Любые подсказки о том, как работать с производными классами в Angular2? Я что-то пропустил или пытался что-то сделать невозможно?

Ответ 1

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

@Component({
  selector: 'child-comp2',
  template: '<div>child component #2</div>',
  providers: [{provide: BaseComponent, useExisting: forwardRef(() => ChildComponent2) }]
})
export class ChildComponent2 extends BaseComponent {
}

@Component({
  selector: 'child-comp1',
  template: '<div>child component #1</div>',
  providers: [{provide: BaseComponent, useExisting: forwardRef(() => ChildComponent1) }]
})
export class ChildComponent1 extends BaseComponent {
}

Plunker: http://plnkr.co/edit/5gb5E4curAE2EfH2lNZQ?p=preview

Ответ 2

Вы можете использовать локальные переменные шаблона с запросом ContentChildren для создания списка всех компонентов с общим базовым классом. Ниже приведен код plunk для кода ниже. Этот подход был взят из предложения в Kara Erickson Ошибки новичков в блоге.

В шаблоне каждый дочерний компонент помечен #child:

  <app-container>
    <app-child1 #child></app-child1>
    <app-child1 #child></app-child1>
    <app-child1 #child></app-child1>
    <app-child1 #child></app-child1>
    <app-child2 #child></app-child2>
  </app-container>

Затем это можно использовать для выбора всех компонентов с общим базовым компонентом:

  import { Component, QueryList, ContentChildren, ViewChildren, AfterViewInit, AfterContentInit } from '@angular/core';
  import { BaseComponent } from '../base/base.component';
  import { Child1Component } from '../child1/child1.component';
  import { Child2Component } from '../child2/child2.component';

  @Component({
    selector: 'app-container',
    templateUrl: './src/container/container.component.html'
  })
  export class ContainerComponent implements AfterContentInit  {
    @ContentChildren('child') allChildren: QueryList<BaseComponent>;
    @ContentChildren(Child1Component) child1Chdilren: QueryList<Child1Component>;
    @ContentChildren(Child2Component) child2Chdilren: QueryList<Child2Component>;

    constructor() { }

    ngOnInit() {
    }

    ngAfterContentInit() {
    }

  }