Angular Material mat-table 在组件中定义可重用列

Posted

技术标签:

【中文标题】Angular Material mat-table 在组件中定义可重用列【英文标题】:Angular Material mat-table define reusable column in component 【发布时间】:2019-04-05 14:44:08 【问题描述】:

任何人都知道是否可以创建一个与 mat-table 一起使用的“列”组件,我尝试为常用的列定义创建一个组件,但是在添加到表时我得到一个无法找到的错误列选择器,我的列定义如下:

@Component(
  selector: 'iam-select-column',
  template: `
  <ng-container matColumnDef="select">
    <mat-header-cell *matHeaderCellDef>
      <mat-checkbox></mat-checkbox>
    </mat-header-cell>
    <mat-cell *matCellDef="let row">
      <mat-checkbox></mat-checkbox>
    </mat-cell>
  </ng-container>
  `,
  styles: [`
  `]
)
export class SelectColumnComponent implements OnInit 

  constructor()  

  ngOnInit() 
  


并在表格中使用它

<mat-table class="mat-elevation-z8">

  <iam-select-column></iam-select-column>

  <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
  <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>

</mat-table>

显示的列是:

  displayedColumns = [
    'select'
  ];

是否可以这样做,因为我想避免在我有选择列的表中重复?

【问题讨论】:

【参考方案1】:

为了使其正常工作,您必须使用 table.addColumnDef 方法手动将 columnDef 添加到表中。

@Component(
  selector: 'iam-select-column',
  template: `
    <ng-container matColumnDef="select">
        ...
    </ng-container>
  `
)
export class SelectColumnComponent implements OnInit 
  @ViewChild(MatColumnDef) columnDef: MatColumnDef;

  constructor(@Optional() public table: MatTable<any>, private cdRef: ChangeDetectorRef)  

  ngOnInit() 
    if (this.table) 
      this.cdRef.detectChanges();
      this.table.addColumnDef(this.columnDef);
    
  

但在这样做之前,我们必须确保matColumnDef 指令已经完成绑定初始化,以便它拥有name。为此,我们必须在该组件上运行 detectChanges。

Ng-run Example

另一种方法是在父组件中提供该名称,如角度材料问题中所述 https://github.com/angular/material2/issues/13808#issuecomment-434417804:

parent.html

<mat-table class="mat-elevation-z8">

  <iam-select-column name="select"></iam-select-column>

选择列组件

@Input()
get name(): string  return this._name; 
set name(name: string) 
    this._name = name;
    this.columnDef.name = name;

【讨论】:

【参考方案2】:

这就是 Angular 12 和 @angular/material 12 对我有用的方法。 此代码基于来自https://github.com/angular/components/issues/5889的代码sn-ps

@Component(
  selector: 'app-column-template',
  template: `
    <ng-container matColumnDef>
      <th mat-header-cell *matHeaderCellDef> label || capitalize(name) </th>
      <td mat-cell *matCellDef="let row">
        <ng-container *ngTemplateOutlet="cellTemplate; context:  $implicit: row "></ng-container>
      </td>
    </ng-container>
  `,
  // eslint-disable-next-line @angular-eslint/no-host-metadata-property
  host: 
    class: 'column-template cdk-visually-hidden',
    '[attr.ariaHidden]': 'true',
  ,
)
export class ColumnTemplateComponent implements OnDestroy, OnInit 
  @Input() name = '';
  @Input() label: string | null = null;
  @Input() align: 'before' | 'after' = 'before';

  constructor(@Optional() public table: MatTable<unknown>) 

  @ViewChild(MatColumnDef,  static: true ) columnDef!: MatColumnDef;
  @ViewChild(MatCellDef,  static: true ) cellDef!: MatCellDef;
  @ViewChild(MatHeaderCellDef,  static: true ) headerCellDef!: MatHeaderCellDef;
  @ViewChild(MatFooterCellDef,  static: true ) footerCellDef!: MatFooterCellDef;

  @ContentChild('cell',  static: false )
  cellTemplate: TemplateRef<unknown> | null = null;

  ngOnInit(): void 
    if (this.table && this.columnDef) 
      this.columnDef.name = this.name;
      this.columnDef.cell = this.cellDef;
      this.columnDef.headerCell = this.headerCellDef;
      this.columnDef.footerCell = this.footerCellDef;
      this.table.addColumnDef(this.columnDef);
    
  

  ngOnDestroy(): void 
    if (this.table) 
      this.table.removeColumnDef(this.columnDef);
    
  

  capitalize(value: string): string 
    return value.charAt(0).toUpperCase() + value.slice(1);
  

export type CellValueNeededFn = (data: Record<string, unknown>, name: string) => string;

@Component(
  selector: 'app-column',
  template: `
    <ng-container matColumnDef>
      <th mat-header-cell *matHeaderCellDef> label || capitalize(name) </th>
      <td mat-cell *matCellDef="let row"> getCellValue(row) </td>
    </ng-container>
  `,
  // eslint-disable-next-line @angular-eslint/no-host-metadata-property
  host: 
    class: 'column cdk-visually-hidden',
    '[attr.ariaHidden]': 'true',
  ,
)
export class ColumnComponent implements OnDestroy, OnInit 
  @Input() name = '';
  @Input() label: string | null = null;
  @Input() align: 'before' | 'after' = 'before';
  @Input() cellValueNeeded: CellValueNeededFn | null = null;

  constructor(@Optional() public table: MatTable<unknown>) 

  @ViewChild(MatColumnDef,  static: true ) columnDef!: MatColumnDef;
  @ViewChild(MatCellDef,  static: true ) cellDef!: MatCellDef;
  @ViewChild(MatHeaderCellDef,  static: true ) headerCellDef!: MatHeaderCellDef;
  @ViewChild(MatFooterCellDef,  static: true ) footerCellDef!: MatFooterCellDef;

  @ContentChild('cell',  static: false )
  cellTemplate: TemplateRef<unknown> | null = null;

  ngOnInit(): void 
    if (this.table && this.columnDef) 
      this.columnDef.name = this.name;
      this.columnDef.cell = this.cellDef;
      this.columnDef.headerCell = this.headerCellDef;
      this.columnDef.footerCell = this.footerCellDef;
      this.table.addColumnDef(this.columnDef);
    
  

  ngOnDestroy(): void 
    if (this.table) 
      this.table.removeColumnDef(this.columnDef);
    
  

  capitalize(value: string): string 
    return value.charAt(0).toUpperCase() + value.slice(1);
  

  getCellValue(row: Record<string, unknown>): unknown 
    return this.cellValueNeeded ? this.cellValueNeeded(row, this.name) : row[this.name];
  

对我来说,基于ColumnTemplateComponent 构建ColumnComponent 的尝试以熟悉的方式结束

Error: Could not find column with id "...".
     at getTableUnknownColumnError (table.js:1078) [angular]
     blah-blah-blah...

【讨论】:

以上是关于Angular Material mat-table 在组件中定义可重用列的主要内容,如果未能解决你的问题,请参考以下文章

Angular Material - mat-table 不渲染来自 rest api 的数据

Angular Material mat 表合并的行

Angular Material 表动态列

Mat-table 多行内多行

包装或扩展 angular-material 组件,允许将未知属性传递给子组件

更改数据库后更新 mat-table 数据