我正在尝试查看是否有可能动态定义mat-table的列,包括列的宽度和对齐方式。
我可以使其与ngStyle一起使用,并且如果使用管道,则不会经常调用代码:
<ng-container *ngFor="let column of columns" matColumnDef="{{column.columnDef}}">
<th mat-header-cell *matHeaderCellDef mat-sort-header >
{{column.header}}
</th>
<td mat-cell *matCellDef="let row" [ngStyle]="column | styleRow">
{{column.cell(row)}}
</td>
</ng-container>
这里styleRow
是一个管道,用于转换列定义列表中的一行并返回该列的正确CSS规则;例如
transform(column: ColumnConfig): object {
const obj = {
textAlign: column.textAlign,
width: column.type === 'number' ?
column.length + 'ch' :
(column.length * 0.45) + 'rem'
};
console.log(column, obj);
return obj;
}
问题是,当我只需要在组件的样式表中放入以下规则的东西时,它仍被称为经常使用的方式:
.mat-column-foo {
width: 7em;
}
.mat-column-bar {
width: 15em;
}
.mat-column-baz {
width: 7em;
text-align: right;
}
// ... and so on.
...但是我不知道该怎么做。在下面的示例中,我希望setStyles函数为:
将css规则添加到我在test.component.css
使用规则创建一个新样式标签,但是我必须找到该组件的_ngcontent_xxxxxx属性,以便创建类似于.mat-column-foo[_ngcontent_xxxxxx] {...}
export interface TblData {
foo: string;
bar: string;
baz: number;
}
export interface ColumnConfig {
columnDef: string;
header: string;
textAlign: string;
length: number;
type: string;
cell: any;
}
const ELEMENT_DATA: TblData[] = [ .... ];
@Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class TestComponent implements OnInit {
columns: ColumnConfig[] = [
{ columnDef: 'foo',
header: 'Foo',
textAlign: 'left',
length: 10,
type: 'date',
cell: (element: any) => `${element.foo}`
},
{ columnDef: 'bar',
header: 'Bar',
textAlign: 'left',
length: 50,
type: 'string',
cell: (element: any) => `${element.bar}`
},
{ columnDef: 'baz',
header: 'Baz',
textAlign: 'right',
length: 7,
type: 'number',
cell: (element: any) => `${element.baz}`
}
];
displayedColumns = ['select', ...this.columns.map(c => c.columnDef)];
dataSource = new MatTableDataSource<TblData>(ELEMENT_DATA);
selection = new SelectionModel<TblData>(true, []);
@ViewChild(MatPaginator, {static: true}) paginator: MatPaginator;
@ViewChild(MatSort, {static: true}) sort: MatSort;
constructor() {
}
ngOnInit() {
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
// setStyles(columns); <-- how do I implement this function ?
}
.....
示例的完整来源:
html:
<div>
<div class="mat-elevation-z8">
<div class="table_scroll">
<div class="table_intro">
<p>
Some text above the table.
</p>
<p>
And more text.
</p>
</div>
<table mat-table [dataSource]="dataSource" matSort>
<!-- Checkbox Column -->
<ng-container matColumnDef="select">
<th mat-header-cell *matHeaderCellDef>
<mat-checkbox (change)="$event ? masterToggle() : null"
[checked]="selection.hasValue() && isAllSelected()"
[indeterminate]="selection.hasValue() && !isAllSelected()"
[aria-label]="checkboxLabel()">
</mat-checkbox>
</th>
<td mat-cell *matCellDef="let row">
<mat-checkbox (click)="$event.stopPropagation()"
(change)="$event ? selection.toggle(row) : null"
[checked]="selection.isSelected(row)"
[aria-label]="checkboxLabel(row)">
</mat-checkbox>
</td>
</ng-container>
<!-- Dynamic Columns -->
<ng-container *ngFor="let column of columns; trackBy: columnId" matColumnDef="{{column.columnDef}}">
<th mat-header-cell *matHeaderCellDef mat-sort-header>
{{column.header}}
</th>
<td mat-cell *matCellDef="let row" [ngStyle]="column | styleRow">
{{column.cell(row) | formatRow:column}}
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
</div>
<mat-toolbar>
<mat-toolbar-row>
<mat-icon title="Export as CSV">table_chart</mat-icon>
<mat-icon title="Export as PDF">print</mat-icon>
<span class="example-spacer"></span>
<mat-paginator class="paginator" [pageSizeOptions]="[50, 100, 200]"></mat-paginator>
</mat-toolbar-row>
</mat-toolbar>
</div>
</div>
TS:
import {ChangeDetectionStrategy, Component, Input, OnInit, ViewChild} from '@angular/core';
import {MatTableDataSource} from '@angular/material/table';
import {SelectionModel} from '@angular/cdk/collections';
import {MatPaginator} from '@angular/material/paginator';
import {MatSort} from '@angular/material/sort';
/**
* Column definition.
* columnDef is the key name in the row record
* header is the text displayed in the column's header
* textAlign and length are for styling the columns
* type is for future use
* cell is a function to get that cell's value from the row.
*/
export interface ColumnConfig {
columnDef: string;
header: string;
textAlign: string;
length: number;
type: string;
cell: any;
}
/**
* Displayed rows must have a selection label property.
*/
export interface MrmTableRow {
selection_label: string;
}
@Component({
selector: 'app-mrm-table',
templateUrl: './mrm-table.component.html',
styleUrls: ['./mrm-table.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MrmTableComponent<T extends MrmTableRow> implements OnInit {
@Input() columns: ColumnConfig[];
@Input() data: T[];
selection = new SelectionModel<T>(true, []);
@ViewChild(MatPaginator, {static: true}) paginator: MatPaginator;
@ViewChild(MatSort, {static: true}) sort: MatSort;
public dataSource: MatTableDataSource<T>;
public displayedColumns: string[];
constructor() {
}
ngOnInit() {
this.dataSource = new MatTableDataSource<T>(this.data);
this.displayedColumns = ['select', ...this.columns.map(c => c.columnDef)];
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
// setStyles(data);
}
/**
* returns true if all rows are selected
*/
isAllSelected() {
const numSelected = this.selection.selected.length;
const numRows = this.dataSource.data.length;
return numSelected === numRows;
}
/**
* Selects all rows if they are not all selected; otherwise clear selection.
*/
masterToggle() {
this.isAllSelected() ?
this.selection.clear() :
this.dataSource.data.forEach(row => this.selection.select(row));
}
/** The aria label (accessibility) for the checkbox on the passed row */
checkboxLabel(row?: T): string {
if (!row) {
return `${this.isAllSelected() ? 'select' : 'deselect'} all`;
}
return `${this.selection.isSelected(row) ?
'deselect' :
'select'} row ${row.selection_label} `;
}
/**
* This for the trackBy in the column loop.
*/
columnId(index: number, item: ColumnConfig): string {
return item.columnDef;
}
}
SCSS:
.padded {
padding: 15px;
background-color: #fafafa;
}
.example-spacer {
flex: 1 1 auto;
}
:host > div {
padding: 10px;
& > div {
.mat-toolbar {
background-color: transparent;
}
width: 48em;
.table_intro {
padding: 25px;
}
div.table_scroll {
height: 600px;
overflow: auto;
}
}
}
styleRow管道供参考,但是我想要一个不需要它的解决方案。
import {Pipe, PipeTransform} from '@angular/core';
import {ColumnConfig} from './mrm-table/mrm-table.component';
@Pipe({
name: 'styleRow'
})
export class StyleRowPipe implements PipeTransform {
transform(column: ColumnConfig): object {
const obj = {
textAlign: column.textAlign,
width: column.type === 'number' ?
column.length + 'ch' :
(column.length * 0.55) + 'rem'
};
console.log('styleRow', column, obj);
return obj;
}
}
答案 0 :(得分:0)
尝试向*ngFor
添加trackBy函数。如果您不使用此方法为每列提供一些标识符,则每次列表引用发生更改时都会重新渲染整个列表。
语法如下:
<ng-container *ngFor="let column of columns; trackBy: getColumnId(column)">
在您的情况下,getColumnId
方法似乎可以返回column.columnDef
。
styleRow
管道仅应在特定列的列引用更改时重新评估。
答案 1 :(得分:0)
我可能已经找到一种做自己想做的方法。
首先,我为模板中的表命名:
<table mat-table [dataSource]="dataSource" matSort #table>
然后,我检索了对组件中表的引用:
@ViewChild('table', { read: ElementRef , static: true}) tableRef: ElementRef;
最后,我得到用于CSS规则隔离的属性,计算CSS规则并注入它们:
setStyles(columns: ColumnConfig[]) {
// Get the specific attribute for the component
const attrNames: string[] = Array.from(this.tableRef.nativeElement.attributes)
.map((val: Attr) => val.name)
.filter((val: string) => val.startsWith('_ngcontent'));
// Hopefully there should be only 1 attribute that starts with _ngcontent.
const attr: string = attrNames[0];
// Now I can make the css rules
const css: string = columns.map((col) => {
const width = col.type === 'number' ?
col.length + 'ch' :
(col.length * 0.55) + 'rem';
return `
.mat-column-${col.columnDef}[${attr}] {
text-align: ${col.textAlign};
width: ${width};
}`;
}).join('\n');
// And inject them in the page
const head = document.getElementsByTagName('head')[0];
const style = document.createElement('style');
style.type = 'text/css';
style.appendChild(document.createTextNode(css));
head.appendChild(style);
}
它有效;现在我必须找到一种方法来删除ngOnDestroy中的样式标签。