在列 'Total' 中排序无效 angular mat-table
Sort in column 'Total' not working angular mat-table
我的 mat table 工作正常,我的所有列都在排序,除了我的最后一列使用 reduce 计算其他列的总和值。
我有点陷入这个问题,首先虽然这可能是由于 reduce 方法发生在 OnInit
生命周期之后,但即使我在 ngAfterViewInit
中声明 this.dataSource.data = this.data
也不起作用.
我的HTML模板:
<div [ngClass]="{'hidden': !hasData()}">
<div class="subcollection-name">{{subCollection.name}}</div>
<div class="table-container">
<table
matSort
mat-table
class="items-table"
[dataSource]="dataSource"
>
<ng-container matColumnDef="coverUrl">
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let item">
<img class="item-image" src="{{item.displayImage}}" onError="this.src='assets/images/no-image-available.png';">
</td>
<td mat-footer-cell *matFooterCellDef>
{{ "collectionReportsSubcollectionTablesTotalRow" | translate }}
<td>
</ng-container>
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef mat-sort-header>
{{ 'collectionRejectionReportRejectedItemTableHeaderId' | translate}}
</th>
<td mat-cell *matCellDef="let item">
<a target="_blank" routerLink="/item/{{item.id}}" class="item-id-link">ID-{{ item.id }}</a>
</td>
<td mat-footer-cell *matFooterCellDef></td>
</ng-container>
<ng-container matColumnDef="name" sticky>
<th mat-header-cell *matHeaderCellDef mat-sort-header>
{{ 'collectionRejectionReportRejectedItemTableHeaderName' | translate}}
</th>
<td mat-cell *matCellDef="let item">{{ item | displayItemName }}</td>
<td mat-footer-cell *matFooterCellDef></td>
</ng-container>
<ng-container *ngFor="let step of stepsName$ | async">
<ng-container matColumnDef="{{step}}">
<th mat-header-cell *matHeaderCellDef mat-sort-header>
{{step}}
</th>
<td mat-cell *matCellDef="let item">{{ 'stepsReportAverageTimeSpentDaysUnit' | translate: { numberDays: getDataOfItemForStepName(item.id, step) } }}</td>
<td mat-footer-cell *matFooterCellDef>{{ 'stepsReportAverageTimeSpentDaysUnit' | translate: { numberDays: getTotalDataOfStep(step) } }}</td>
</ng-container>
</ng-container>
<ng-container matColumnDef="total" stickyEnd>
<th mat-header-cell *matHeaderCellDef mat-sort-header>
{{ "collectionReportsSubcollectionTablesTotalCollumn" | translate }}
</th>
<td mat-cell *matCellDef="let item">{{ 'stepsReportAverageTimeSpentDaysUnit' | translate:{ numberDays: getTotalDataOfItem(item.id) } }}</td>
<td mat-footer-cell *matFooterCellDef>{{ 'stepsReportAverageTimeSpentDaysUnit' | translate:{ numberDays: getTotalDataOfAll() } }}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="columns; sticky: true"></tr>
<tr mat-row *matRowDef="let row; columns: columns;"></tr>
<tr mat-footer-row *matFooterRowDef="columns"></tr>
</table>
</div>
<mat-paginator [pageSizeOptions]="[5, 10, 25, 100]"></mat-paginator>
</div>
TypeScript 文件
import { Observable, BehaviorSubject } from 'rxjs';
import { SubCollection } from 'model/item-tree/collection';
import { MatSort, MatPaginator, MatTableDataSource } from '@angular/material';
import { Component, ViewChild, Input, OnInit, ChangeDetectionStrategy, AfterViewInit, OnChanges } from '@angular/core';
@Component({
selector: 'app-collection-steps-report-subcollection-table',
templateUrl: './collection-steps-report-subcollection-table.component.html',
styleUrls: ['./collection-steps-report-subcollection-table.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class CollectionStepsReportSubcollectionTableComponent implements OnInit {
@ViewChild(MatSort, { static: true }) sort: MatSort;
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
@Input() data: any[];
@Input() subCollection: SubCollection;
@Input() filterBy$: Observable<string>;
@Input() stepsName$: BehaviorSubject<string[]>;
public columns: string[];
public dataSource = new MatTableDataSource();
public ngOnInit(): void {
this.dataSource.filterPredicate = (data: any, filter: string): boolean => {
if (filter === 'id') {
return true;
}
const id = data['id'] as string;
const name = data['name'] as string;
const matchId = (id) ? id.trim().toLowerCase().indexOf(filter) !== -1 : true;
const matchName = (name) ? name.trim().toLowerCase().indexOf(filter) !== -1 : true;
return matchName || matchId;
};
this.dataSource.sort = this.sort;
this.dataSource.paginator = this.paginator;
this.filterBy$.subscribe(
filter => {
this.dataSource.filter = filter.trim().toLowerCase();
if (this.dataSource.paginator) {
this.dataSource.paginator.firstPage();
}
}
);
this.stepsName$.subscribe(
sn => {
this.columns = ['coverUrl', 'id', 'name', ...sn, 'total'];
}
);
this.dataSource.data = this.data;
}
public hasData(): boolean {
return Array.isArray(this.data) && this.data.length > 0;
}
public getTotalDataOfItem(itemId: string): number {
const item = this.data.find(i => i.id === itemId);
return this.stepsName$.getValue().reduce((acc, stepName) => {
return acc += item[stepName];
}, 0);
}
public getTotalDataOfAll(): number {
return this.data.reduce(
(acc1, item) => acc1 += this.stepsName$.getValue().reduce((acc2, stepName) => acc2 += item[stepName], 0)
, 0);
}
public getDataOfItemForStepName(itemId: string, stepName: string): string {
return this.data.find(i => i.id === itemId)[stepName];
}
public getTotalDataOfStep(step: string): number {
return this.data.reduce((acc, item) => acc += item[step], 0);
}
}
很高兴有任何提示或帮助澄清我的想法以解决此问题:) 谢谢!
经过一些尝试,我发现我的问题是列 'total' 不是 'item' 对象的一部分。
所以我对我的数据实施了一个 map
方法,将 getTotalDataOfItem
函数的 return 分配给 ngOnInit
中的新数据数组,它工作正常!
由于我的项目现在有 'total' 列,我不得不重构 HTML 模板以在 <td>
中呈现 item.total
而不是 return getTotalDataOfItem(item.id)
要使事情正常进行,只需在 TS 文件 ngOnInit
中添加此代码
const dataWithTotal = this.data.map(
value => {
return {
...value, total: this.getTotalDataOfItem(value['id'])
};
}
);
然后像这样重构 HTML 文件
<td mat-cell *matCellDef="let item">{{ 'stepsReportAverageTimeSpentDaysUnit' | translate:{ numberDays: item.total } }}</td>
然后享受 mat-table 正确排序所有列的乐趣:)[=19=]
我的 mat table 工作正常,我的所有列都在排序,除了我的最后一列使用 reduce 计算其他列的总和值。
我有点陷入这个问题,首先虽然这可能是由于 reduce 方法发生在 OnInit
生命周期之后,但即使我在 ngAfterViewInit
中声明 this.dataSource.data = this.data
也不起作用.
我的HTML模板:
<div [ngClass]="{'hidden': !hasData()}">
<div class="subcollection-name">{{subCollection.name}}</div>
<div class="table-container">
<table
matSort
mat-table
class="items-table"
[dataSource]="dataSource"
>
<ng-container matColumnDef="coverUrl">
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let item">
<img class="item-image" src="{{item.displayImage}}" onError="this.src='assets/images/no-image-available.png';">
</td>
<td mat-footer-cell *matFooterCellDef>
{{ "collectionReportsSubcollectionTablesTotalRow" | translate }}
<td>
</ng-container>
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef mat-sort-header>
{{ 'collectionRejectionReportRejectedItemTableHeaderId' | translate}}
</th>
<td mat-cell *matCellDef="let item">
<a target="_blank" routerLink="/item/{{item.id}}" class="item-id-link">ID-{{ item.id }}</a>
</td>
<td mat-footer-cell *matFooterCellDef></td>
</ng-container>
<ng-container matColumnDef="name" sticky>
<th mat-header-cell *matHeaderCellDef mat-sort-header>
{{ 'collectionRejectionReportRejectedItemTableHeaderName' | translate}}
</th>
<td mat-cell *matCellDef="let item">{{ item | displayItemName }}</td>
<td mat-footer-cell *matFooterCellDef></td>
</ng-container>
<ng-container *ngFor="let step of stepsName$ | async">
<ng-container matColumnDef="{{step}}">
<th mat-header-cell *matHeaderCellDef mat-sort-header>
{{step}}
</th>
<td mat-cell *matCellDef="let item">{{ 'stepsReportAverageTimeSpentDaysUnit' | translate: { numberDays: getDataOfItemForStepName(item.id, step) } }}</td>
<td mat-footer-cell *matFooterCellDef>{{ 'stepsReportAverageTimeSpentDaysUnit' | translate: { numberDays: getTotalDataOfStep(step) } }}</td>
</ng-container>
</ng-container>
<ng-container matColumnDef="total" stickyEnd>
<th mat-header-cell *matHeaderCellDef mat-sort-header>
{{ "collectionReportsSubcollectionTablesTotalCollumn" | translate }}
</th>
<td mat-cell *matCellDef="let item">{{ 'stepsReportAverageTimeSpentDaysUnit' | translate:{ numberDays: getTotalDataOfItem(item.id) } }}</td>
<td mat-footer-cell *matFooterCellDef>{{ 'stepsReportAverageTimeSpentDaysUnit' | translate:{ numberDays: getTotalDataOfAll() } }}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="columns; sticky: true"></tr>
<tr mat-row *matRowDef="let row; columns: columns;"></tr>
<tr mat-footer-row *matFooterRowDef="columns"></tr>
</table>
</div>
<mat-paginator [pageSizeOptions]="[5, 10, 25, 100]"></mat-paginator>
</div>
TypeScript 文件
import { Observable, BehaviorSubject } from 'rxjs';
import { SubCollection } from 'model/item-tree/collection';
import { MatSort, MatPaginator, MatTableDataSource } from '@angular/material';
import { Component, ViewChild, Input, OnInit, ChangeDetectionStrategy, AfterViewInit, OnChanges } from '@angular/core';
@Component({
selector: 'app-collection-steps-report-subcollection-table',
templateUrl: './collection-steps-report-subcollection-table.component.html',
styleUrls: ['./collection-steps-report-subcollection-table.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class CollectionStepsReportSubcollectionTableComponent implements OnInit {
@ViewChild(MatSort, { static: true }) sort: MatSort;
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
@Input() data: any[];
@Input() subCollection: SubCollection;
@Input() filterBy$: Observable<string>;
@Input() stepsName$: BehaviorSubject<string[]>;
public columns: string[];
public dataSource = new MatTableDataSource();
public ngOnInit(): void {
this.dataSource.filterPredicate = (data: any, filter: string): boolean => {
if (filter === 'id') {
return true;
}
const id = data['id'] as string;
const name = data['name'] as string;
const matchId = (id) ? id.trim().toLowerCase().indexOf(filter) !== -1 : true;
const matchName = (name) ? name.trim().toLowerCase().indexOf(filter) !== -1 : true;
return matchName || matchId;
};
this.dataSource.sort = this.sort;
this.dataSource.paginator = this.paginator;
this.filterBy$.subscribe(
filter => {
this.dataSource.filter = filter.trim().toLowerCase();
if (this.dataSource.paginator) {
this.dataSource.paginator.firstPage();
}
}
);
this.stepsName$.subscribe(
sn => {
this.columns = ['coverUrl', 'id', 'name', ...sn, 'total'];
}
);
this.dataSource.data = this.data;
}
public hasData(): boolean {
return Array.isArray(this.data) && this.data.length > 0;
}
public getTotalDataOfItem(itemId: string): number {
const item = this.data.find(i => i.id === itemId);
return this.stepsName$.getValue().reduce((acc, stepName) => {
return acc += item[stepName];
}, 0);
}
public getTotalDataOfAll(): number {
return this.data.reduce(
(acc1, item) => acc1 += this.stepsName$.getValue().reduce((acc2, stepName) => acc2 += item[stepName], 0)
, 0);
}
public getDataOfItemForStepName(itemId: string, stepName: string): string {
return this.data.find(i => i.id === itemId)[stepName];
}
public getTotalDataOfStep(step: string): number {
return this.data.reduce((acc, item) => acc += item[step], 0);
}
}
很高兴有任何提示或帮助澄清我的想法以解决此问题:) 谢谢!
经过一些尝试,我发现我的问题是列 'total' 不是 'item' 对象的一部分。
所以我对我的数据实施了一个 map
方法,将 getTotalDataOfItem
函数的 return 分配给 ngOnInit
中的新数据数组,它工作正常!
由于我的项目现在有 'total' 列,我不得不重构 HTML 模板以在 <td>
中呈现 item.total
而不是 return getTotalDataOfItem(item.id)
要使事情正常进行,只需在 TS 文件 ngOnInit
中添加此代码
const dataWithTotal = this.data.map(
value => {
return {
...value, total: this.getTotalDataOfItem(value['id'])
};
}
);
然后像这样重构 HTML 文件
<td mat-cell *matCellDef="let item">{{ 'stepsReportAverageTimeSpentDaysUnit' | translate:{ numberDays: item.total } }}</td>
然后享受 mat-table 正确排序所有列的乐趣:)[=19=]