Declare a component with generic type

我的梦境 提交于 2019-12-04 15:20:27

问题


Is it possible to declare a component with a generic type in Angular 4?

The following code causes build errors:

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

    //...
}

The error when executing ng serve is:

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

Example:

The following example is an attempt to implement a generic data table where @Input() data changes from one component 'calling this component' to another. The question is could BehaviorSubject<any[]> be changed to BehaviorSubject<T[]> where T would be the generic type passed to the component?

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

回答1:


You can also access the Type parameter through the ViewChild like this:

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



回答2:


You can declare it, but cannot use it directly. You can do something like this:

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



回答3:


You can consider this way. Create an interface for the data such as following:

interface ListItem {
  info: string;
  ...
}

Transform the data that you want to list to comply with the interface and thus can be interpreted by the ListDataComponent. Your ListDataComponent can then list the data according to the properties in the interface.

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



回答4:


I would recommend creating a parent list component with multiple child components for each type of data displayed, then use [ngSwitch] and *ngSwitchCase to determine what to display.

@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
}



来源:https://stackoverflow.com/questions/46893991/declare-a-component-with-generic-type

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!