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
が1つのコンポーネントを「このコンポーネントを呼び出す」から別のコンポーネントに変更する一般的なデータテーブルを実装する試みです。質問は、_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() {}
}
_
次のように、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);
}
}
宣言できますが、直接使用することはできません。次のようなことができます:
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() {
}
}
表示されるデータの種類ごとに複数の子コンポーネントを持つ親リストコンポーネントを作成し、[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
}