声明具有泛型类型的组件

时间:2017-10-23 16:16:46

标签: angular typescript angular-components typescript-generics

是否可以在Angular 4中声明具有泛型类型的组件?

以下代码导致构建错误:

export class MyGenericComponent<T> implements OnInit {
    @Input()  data: BehaviorSubject<T[]>;

    //...
}

执行ng serve时的错误是:

ERROR in C:/.../my-generic.module.ts (5,10): Module '"C:/.../my-generic.component"' has no exported member 'MyGenericComponent'.

示例:

以下示例尝试实现通用数据表,其中@Input() data从一个组件更改并调用此组件&#39;到另一个。 问题是BehaviorSubject<any[]>可以更改为BehaviorSubject<T[]>,其中T是传递给组件的通用类型吗?

@Component({
  selector: 'my-data-list',
  templateUrl: './data-list.component.html',
  styleUrls: ['./data-list.component.css']
})
export class DataListComponent implements OnInit {
  @Input()  data: BehaviorSubject<any[]>;
  @Output() onLoaded = new EventEmitter<boolean>();

  private tableDataBase : TableDataBase = new TableDataBase();
  private dataSource : TableDataSource | null;

  constructor() { }

  ngOnInit() {
    this.tableDataBase.dataChange = this.data;
    this.dataSource = new TableDataSource(this.tableDataBase);
    this.onLoaded.emit(true);
  }
}

class TableDataBase {
  dataChange: BehaviorSubject<any[]> = new BehaviorSubject<any[]>([]);

  get data(): any[] {
    return this.dataChange.value;
  }
}

class TableDataSource extends DataSource<any> {

  constructor(private tableDataBase: TableDataBase) {
    super();
  }

  connect(): Observable<any[]> {
    return Observable.of(this.tableDataBase.data);
  }

  disconnect() {}
}

4 个答案:

答案 0 :(得分:6)

您可以声明它,但不能直接使用它。你可以这样做:

export abstract class Form<T> implements OnInit, OnChanges {
  someMethod() { throw 'Dont use directly' }
  otherMethod() { return 'Works!'; }
  // Note that below will cause compilation error
  //   TypeError: Object prototype may only be an Object or null: undefined
  // You cannot use protected in this usecase
  protected anotherMethod() { }
}

@Component({})
export class ModelOneForm extends Form<ModelOne> {
  someMethod() { return this.otherMethod(); }
}
 

答案 1 :(得分:0)

你可以这样考虑。为数据创建接口,如下所示:

interface ListItem {
  info: string;
  ...
}

转换要列出的数据以符合接口,因此可以由ListDataComponent解释。然后,ListDataComponent可以根据界面中的属性列出数据。

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

@Component({
  selector: 'data-list',
  templateUrl: './data-list.component.html',
  styleUrls: ['./data-list.component.scss']
})
export class DataListComponent implements OnInit {
    @Input() public items: ListItem[];

    constructor() {
    }

    ngOnInit() {
    }
}

答案 2 :(得分:0)

我建议为每种显示的数据类型创建一个具有多个子组件的父列表组件,然后使用[ngSwitch]*ngSwitchCase确定要显示的内容。

@Component({
  selector: 'app-list',
  template: `
    <ng-container *ngFor="let item in list$ | async" [ngSwitch]="item.type">
      <app-list-item-one [item]="item" *ngSwitchCase="listItemType.One"></app-list-item-one>
      <app-list-item-two [item]="item" *ngSwitchCase="listItemType.Two"></app-list-item-two>
    </ng-container>
  `
})
export class ListComponent extends OnInit {
  list$: Observable<ListItem[]>

  constructor(
    private listApi: ListApiService
  ) { }

  ngOnInit() {
    this.list$ = this.listApi.getList(...)
  }
}

@Component({
  selector: 'app-list-item-one',
  template: `
    {{ item.aProperty }}
  `
})
export class ListItemOneComponent {
  @Input() item: ListItemOne
}

@Component({
  selector: 'app-list-item-two',
  template: `
    {{ item.bProperty }}
  `
})
export class ListItemTwoComponent {
  @Input() item: ListItemTwo
}

export class ListItem {
  id: string
}

export class ListItemOne {
  aProperty: string
}

export class ListItemTwo {
  bProperty: string
}

答案 3 :(得分:0)

我做了与Jun711类似的事情。我创建了一个接口,然后我的组件使用了该接口。然后,我只是扩展其他类的接口。我只是传入了扩展接口类型的数组。

export interface INameable {
    name: string;
}

export interface IPerson extends INameable { title: string; }
export interface IManager extends INameable { Employees: IPerson[]; }

@Component({
    selector: 'nameable',
    templateUrl: './nameable.component.html',
    styleUrls: ['./nameable.component.scss'],
})
export class NameableComponent implements OnInit {

    @Input() names: INameable[] = [];
    @Output() selectedNameChanged = new EventEmitter<INameable>();

    constructor() {}
    ngOnInit() {}
}

然后用法很简单:

<nameable [names]="PersonList" (selectedNameChanged)="personChangedHandler($event)"></nameable>
<nameable [names]="ManagerList" (selectedNameChanged)="mangagerChangedHandler($event)"></nameable>

不利之处在于,包含组件必须确定完整类型,但是不利的是,随着我遵循Liskov和ISP,我的组件变得更加可重用。