使用泛型类型声明组件

是否可以在角度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从一个组件“调用此组件”更改为另一个组件。 问题是 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() {}
}
60491 次浏览

您可以声明它,但不能直接使用它:

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

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

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

您还可以通过 ViewChild 访问 Type 参数,如下所示:

export class Bazz {
name: string;


constructor(name: string) {
this.name = name;
}
}


@Component({
selector: 'app-foo',
template: `<div>\{\{bazz?.name}}</div>`,
exportAs: 'appFoo'
})
export class FooComponent<T> {
constructor() {}
private _bazz: T;


set bazz(b: T) {
this._bazz = b;
}


get bazz(): T {
return this._bazz;
}
}


@Component({
selector: 'app-bar',
template: `<app-foo #appFoo></app-foo>`,
styleUrls: ['./foo.component.scss'],
})
export class BarComponent<T> implements OnInit {
@ViewChild('appFoo') appFoo: FooComponent<Bazz>;


constructor() {}


ngOnInit() {
this.appFoo.bazz = new Bazz('bizzle');
console.log(this.appFoo.bazz);
}
}

我建议为每种显示的数据类型创建一个包含多个子组件的父列表组件,然后使用 [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
}


我做了一些类似于6月711日。我创建了一个接口,然后我的组件使用该接口。然后我只扩展其他类的接口。我只是传入一个类型的数组来扩展接口。

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,我的组件变得更可重用。