【问题标题】:Angular Material mat-table define reusable column in componentAngular Material mat-table 在组件中定义可重用列
【发布时间】: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'
  ];

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

【问题讨论】:

    标签: angular angular-material2 mat-table


    【解决方案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...
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-05-25
        • 1970-01-01
        • 1970-01-01
        • 2019-10-10
        • 1970-01-01
        • 1970-01-01
        • 2020-06-19
        • 1970-01-01
        相关资源
        最近更新 更多