html - Angular Material tablosuna dinamik sütun başlıkları ekle
Şu anda Angular projesi içinde, proje için geliştirdiğim yeniden kullanılabilir bileşenleri barındıran bir paket kullanıyorum. Material tablodaki sütun başlıklarını/satırları dinamik hale getirmek için ne yapabilirim, böylece tabloyu Angular projem içinde kullanırken bunları bir giriş dizisi olarak geçirebilirim?
Bu başlıklardan bazıları üst üste yığılmış veya benzersiz HTML/CSS'e sahip, işte sorunla karşılaştığım yer burası. Bir sütun başlığının/satırın üst üste iki alan barındırıp barındırmayacağını belirten bir boolean flag içeren bir diziyi bileşenimin içinde oluşturmaya çalıştım.
Aşağıda, dinamik olmayan mevcut HTML’imden bir kesit bulunmaktadır. İki ng-container’ın farklı olduğunu görebilirsiniz. Bunu, projemde tablomla çalışırken sadece bir sütun/satır dizisini girdi olarak geçebileceğim şekilde nasıl yazabilirim?
<ng-container matColumnDef="from">
<th mat-header-cell *matHeaderCellDef>
<div [ngStyle] = "{'color': pickupHeader}" class = "stackedColumn">
<span (click)="toggleDates($event)">{{ 'shipperFrom' }}</span>
</div>
<div [ngStyle] = "{'color': deliveryHeader}" class = "stackedColumn">
<span (click) = "toggleDates($event)">{{ 'shipperTo' }}</span>
</div>
</th>
<td mat-cell *matCellDef="let element">
<div>
<span class = "location"> <img src="{{ element.Flag }}">{{element.PickupCity}}</span>
</div>
<div>
<span class = "location"><img src="{{ element.Flag }}">{{element.DeliveryCity}}</span>
</div>
</td>
</ng-container>
<ng-container matColumnDef="legs">
<th mat-header-cell *matHeaderCellDef> {{ somethingElse }} </th>
<td mat-cell *matCellDef="let element"> {{element.SomethingElse}} </td>
</ng-container>
Temelde component.ts dosyamda şöyle bir şey yapmak istiyorum:
data = [{},{},{},{}]
ve bu nesne dizisinin tabloyu doldurmasını ve hangi tür HTML kullanması gerektiğini bilmek istiyorum, böylece bunu içe aktardığımda ve projemde kullandığımda ihtiyacım olan tek şey bu:
<the-table [dataSource] = "data"></the-table>
Temelde, paketi geri dönüp düzenlemek zorunda kalmadan tabloya anında sütun/satır ekleyebilmek istiyorum.
Cevap #1
Ben de benzer bir şey yaptım. Aşağıdakiyle biraz oynadığımı unutma, bazı proje ile ilgili detayları çıkarmak için, bu yüzden herhangi bir sorun olursa bana bildirin.
component.html:
<mat-table matSort [dataSource]="dataSource">
<ng-container *ngFor="let column of displayedColumns; index as i" [matColumnDef]="column">
<mat-header-cell *matHeaderCellDef class="col-search" fxLayoutAlign="start start" fxLayout="column">
<span mat-sort-header>{{ getColumnLabel(column) }}</span>
<!--The below is incomplete Just remove if you don't need filtering-->
<input
matInput
autocomplete="off"
id="{{ column + i }}"
(keyup)="myCustomFilterCode()"
placeholder="Filter"
/>
</mat-header-cell>
<mat-cell *matCellDef="let row">{{ row[column] }}</mat-cell>
</ng-container>
<!--This ngStyle is specifically for fixedWidth tables which are meant to be horizontally scrollable-->
<mat-header-row [ngStyle]="{ 'min-width.px': width ? width : null }" *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row [ngStyle]="{ 'min-width.px': width ? width : null }" *matRowDef="let row; columns: displayedColumns"> </mat-row>
</mat-table>
<mat-paginator [pageSizeOptions]="pageSizeOptions" showFirstLastButtons [pageSize]="startingPageSize"></mat-paginator>
bileşen.ts
/**This component will dynamically create a mat-table for a set of data */
@Component({
selector: 'dynamic-mat-table',
templateUrl: './dynamic-mat-table.component.html',
styleUrls: ['./dynamic-mat-table.component.scss'],
providers: [DatePipe]
})
export class DynamicMatTableComponent implements OnInit, OnChanges {
/**The data to generate a table for */
@Input()
tableData: Object[];
/** This will override the column names in the table, the id will be the property name (i.e. propertyID)
* and the value will be the column header text
*
* **This is Only Necessary if you don't like the Automatically created column names**
*/
@Input()
columnLabels: IdValuePair[];
/**List of column property names (i.e. propertyID) to ignore */
@Input()
ignoreColumns: string[];
/**Sets the starting page size for the paginator */
@Input()
startingPageSize: number;
/**Sets the page size options for the paginator */
@Input()
pageSizeOptions: number[];
/**Defaults to false, when true the table will be generated with a width of # of columns * columnWidth,
* otherwise column widths will not be specified (will fill container with equal width columns) */
@Input()
fixedWidth = false;
/**Defaults to 250, Only used when fixedWidth = true if not set this determines the width of each column */
@Input()
columnWidth = 250;
width: number;
dataSource = new MatTableDataSource<Object>();
fullColumnLabels: IdValuePair[] = [];
displayedColumns: string[];
@ViewChild(MatPaginator, { static: true })
paginator: MatPaginator;
@ViewChild(MatSort, { static: true })
sort: MatSort;
constructor(private datePipe: DatePipe) {}
ngOnInit() {}
/**Generate dynamic table whenever inputs change */
ngOnChanges() {
this.initTable();
if (this.tableData && this.tableData.length > 0) {
this.displayedColumns = Object.keys(this.tableData[0]);
this.removeIgnoredColumns();
this.createLabelsForColumns(this.displayedColumns);
this.calculateRowWidth(this.displayedColumns.length);
this.dataSource.data = this.pipeData([...this.tableData]);
}
}
/**Create the labels array for each column */
private createLabelsForColumns(columns: string[]) {
this.fullColumnLabels = this.columnLabels;
if (this.fullColumnLabels === undefined) {
this.fullColumnLabels = [];
}
if (this.tableData && this.tableData.length > 0) {
columns.forEach(x => {
if (!this.fullColumnLabels.some(label => label.id === x)) {
this.fullColumnLabels.push(new IdValuePair(x, _.startCase(x)));
}
});
}
}
/**Remove ignored columns to prevent from being displayed */
private removeIgnoredColumns() {
if (this.ignoreColumns) {
this.displayedColumns = this.displayedColumns.filter(x => !this.ignoreColumns.some(y => y === x));
}
}
/**Calculate the row width by the number of columns */
private calculateRowWidth(columnNumber: number) {
if (this.fixedWidth) {
this.width = columnNumber * this.columnWidth;
}
}
/**Initialize table */
private initTable() {
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
}
/**Cleans up data with pipes if necessary*/
private pipeData(data: Object[]): Object[] {
data = this.pipeDates(data);
return data;
}
/**Pipe dates through a date pipe if the property name contains 'date' and the value looks like a date*/
private pipeDates(data: Object[]): Object[] {
// ISO_8601 is what .net core returns, may need to expand this list
const formats = [moment.ISO_8601];
// Loop through each row of data
data.forEach((row, index) => {
// Loop through each property in each row
Object.keys(data[index]).forEach(propertyName => {
// Get the value of the property
const value = data[index][propertyName];
// If the value matches a format in the format list, then transform it to a short date
if (propertyName.toLowerCase().search('date') !== -1 && moment(value, formats, true).isValid()) {
data[index][propertyName] = this.datePipe.transform(value, 'short');
}
});
});
return data;
}
/**Gets the label for a given column from the property name */
getColumnLabel(column: string): string {
return this.fullColumnLabels.find(x => x.id === column).value;
}
}
