在列 '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=]