Angular Material mat-table 在组件中定义可重用列
Angular Material mat-table define reusable column in component
任何人都知道是否可以创建一个“列”组件用于 mat-table,我已经尝试为常用的列定义创建一个组件但是当添加到 table 我收到无法找到列的错误 select 或者,我的列定义如下:
@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() {
}
}
并在 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>
显示的列为:
displayedColumns = [
'select'
];
是否可以这样做,因为我想避免在 table 中有一个 select 列的重复?
为了使其正常工作,您必须使用 table.addColumnDef
方法将 columnDef
手动添加到 table。
@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
。为此,我们必须 运行 检测该组件上的变化。
另一种方法是在 angular material 问题中描述的父组件中提供该名称
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;
}
以下是 Angular 12 和 @angular/material 12 对我有用的方法。
此代码基于 https://github.com/angular/components/issues/5889
中的代码片段
@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...
任何人都知道是否可以创建一个“列”组件用于 mat-table,我已经尝试为常用的列定义创建一个组件但是当添加到 table 我收到无法找到列的错误 select 或者,我的列定义如下:
@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() {
}
}
并在 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>
显示的列为:
displayedColumns = [
'select'
];
是否可以这样做,因为我想避免在 table 中有一个 select 列的重复?
为了使其正常工作,您必须使用 table.addColumnDef
方法将 columnDef
手动添加到 table。
@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
。为此,我们必须 运行 检测该组件上的变化。
另一种方法是在 angular material 问题中描述的父组件中提供该名称 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;
}
以下是 Angular 12 和 @angular/material 12 对我有用的方法。 此代码基于 https://github.com/angular/components/issues/5889
中的代码片段@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...