Search code examples
angularangular-material2mat-table

Angular Material mat-table define reusable column in component


Anybody know if it is possible to create a “column” component for use with mat-table, I have tried creating a component for a commonly used column definition but when adding to the table i get an error that was unable to find the column selector, my column definition is below:

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

}

and using this in the table

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

and the displayedColumns are:

  displayedColumns = [
    'select'
  ];

Is it possible to do this as I would like to avoid the duplication in tables where I have a select column?


Solution

  • In order to make it work you have to add that columnDef manually to table by using table.addColumnDef method.

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

    But before doing this we have to make sure that matColumnDef directive has already finished bindings initialization so that it has name. For that we have to run detectChanges on that component.

    Ng-run Example

    Another way is to provide that name in parent component as it described in angular material issue 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>
    

    SelectColumnComponent

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