angular material 分页不适用于可扩展行

angular material pagination not working with expandable row

我正在尝试将 angular material table 与带有分页器的可扩展行一起使用,问题是,对于用于可扩展行的自定义连接 MatTableDataSource,分页停止工作,我没有随时随地找到问题的答案。

因此,测试 table 数据由此存根服务提供:

import { Injectable } from '@angular/core';
import { TableRow } from './table-row';
import { Observable, of } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class TestTableService {

  private testData : TableRow[] = [
    {id:1, name:'pi', value:3.14},
    {id:2, name:'euler', value:2.71},
    {id:3, name:'Feigenbaum constant', value:2.50290},
    {id:4, name:'Mills constant', value:1.30637},
    {id:5, name:'one', value:1},
    {id:6, name:'square root of 2', value:1.41421}
  ];

  constructor() { }

  public getData() : Observable<TableRow[]>
  {
    return of(this.testData);
  }
}

table 组件是:

import { Component, OnInit, ViewChild } from '@angular/core';
import { TestTableService } from './../test-table.service';
import { TableRow } from './../table-row';
import {MatPaginator, MatTableDataSource} from '@angular/material';
import { Observable, of} from 'rxjs';
import { animate, state, style, transition, trigger } from '@angular/animations';

@Component({
  selector: 'app-test-table',
  templateUrl: './test-table.component.html',
  styleUrls: ['./test-table.component.css'],
  animations: [
    trigger('detailExpand', [
      state('collapsed', style({ height: '0px', minHeight: '0', visibility: 'hidden' })),
      state('expanded', style({ height: '*', visibility: 'visible' }))
    ]),
  ],
})
export class TestTableComponent implements OnInit {
  tableData : myDataSource<TableRow> = new myDataSource();
  columnsToDisplay = ['id', 'name','value'];
  isExpansionDetailRow = (i: number, row: Object) => row.hasOwnProperty('detailRow');
  expandedElement = null;

  constructor(private testTable : TestTableService) { }

  @ViewChild(MatPaginator) set matPaginator(mp: MatPaginator) {
    this.paginator = mp;
    this.tableData.paginator = this.paginator;
  }

  //@ViewChild(MatPaginator) paginator: MatPaginator;

  ngOnInit() {
    this.testTable.getData()
      .subscribe(table => this.tableData.data = table);
    //this.tableData.paginator = this.paginator;
  }

  expand(row){
    this.expandedElement = this.expandedElement == row ? null : row;
  }

  //setTimeout(() => this.tableData.paginator = this.paginator);

}

注意我已经尝试了很多涉及分配分页器的解决方案

组件的html:

<table mat-table [dataSource]="tableData">
  <ng-container matColumnDef="id">
    <th mat-header-cell *matHeaderCellDef> Id </th>
    <td mat-cell *matCellDef="let row"> {{row.id}} </td>
  </ng-container>
  <ng-container matColumnDef="name">
    <th mat-header-cell *matHeaderCellDef> Name </th>
    <td mat-cell *matCellDef="let row"> {{row.name}} </td>
  </ng-container>
  <ng-container matColumnDef="value">
    <th mat-header-cell *matHeaderCellDef> Value </th>
    <td mat-cell *matCellDef="let row"> {{row.value}} </td>
  </ng-container>

  <ng-container matColumnDef="details">
    <td mat-cell *matCellDef="let detail"> 
      <!--
      <app-test-table-detail [show]="detail.element == expandedElement" ></app-test-table-detail>
        -->
    </td>
  </ng-container>

  <tr mat-header-row *matHeaderRowDef="columnsToDisplay"></tr>
  <tr mat-row *matRowDef="let row; columns: columnsToDisplay"
    matRipple
    (click)="expand(row)"></tr>

  <tr mat-row *matRowDef="let row; columns: ['details']; when:isExpansionDetailRow"
    [@detailExpand]="row.element==expandedElement ? 'expanded' : 'collapsed'"
    style="overflow: hidden"></tr>
</table>

<mat-paginator [pageSizeOptions]="[3, 10, 20]" showFirstLastButtons></mat-paginator>

有趣的是,分页器给出了正确的数字但没有效果,我应该在流中处理它吗?在连接函数中?

[编辑] stackblitz

的最新代码

https://stackblitz.com/edit/angulartableexample?file=src%2Fapp%2Ftest-table%2Ftest-table.component.html

我也试过删评论的人推荐的AfterViewInit,也没用

[编辑] 通过调试一个没有这个问题的案例(没有可扩展行)不同之处在于这段代码,使用我自己版本的数据源的可扩展行没有观察者,但是使用本地有,我错过了什么在我的数据源定义中?

看来angular material中的table不是那么完整或灵活,所以我通过使用原生table标签和暴力数据处理解决了这个问题。原生 table 被认为是一个很好的选择 in this angular material github issue and also here

我知道它看起来很难看,但解决了这个问题,因为这个组件只应该以完整的方式处理 table(多列 header + 排序 + 过滤 + 可扩展行)尽可能响应,组件内的额外代码不是问题。

我还在视图中使用分页器作为一个简单的小部件,为控制器提供分页数据。

最后,组件视图定义为:

<table class="mat-table" style="overflow-y:scrol">
  <thead>
    <tr class="mat-header-row">
      <th rowspan="2" class="mat-header-cell" (click)="orderBy('id')">
        ID
      </th>
      <th colspan="2" rowspan="1" class="mat-header-cell">
        But can you do this?
      </th>
    </tr>
    <tr class="mat-header-row">
      <th class="mat-header-cell" (click)="orderBy('name')">
        Name
      </th>
      <th class="mat-header-cell" (click)="orderBy('value')">
        Value
      </th>
    </tr>
  </thead>
  <tbody>
    <tr class="mat-row" *ngFor="let element of showData" (click)="expand(element)">
      <td class="mat-cell"  *ngIf="element.detailRow == undefined">
        {{element.id}}
      </td>
      <td class="mat-cell" *ngIf="element.detailRow == undefined">
        {{element.name}}
      </td>
      <td class="mat-cell" *ngIf="element.detailRow == undefined">
        {{element.value}}
      </td>
      <td colspan="3" class="mat-cell" *ngIf="element.detailRow">
        <app-test-table-detail [show]="true" [id]="element.element.id"></app-test-table-detail>
      </td>
    </tr>
  </tbody>
</table>

<mat-paginator [pageSizeOptions]="[3, 10, 20]" showFirstLastButtons (page)="applyPaging()"></mat-paginator>

和控制器,但过滤尚未实现

import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core';
import { TableRow } from './../table-row';
import { PageEvent, MatPaginator } from "@angular/material";
import { TestTableService } from './../test-table.service';

@Component({
  selector: 'app-native-table',
  templateUrl: './native-table.component.html',
  styleUrls: ['./native-table.component.css']
})
export class NativeTableComponent implements AfterViewInit, OnInit {

  private order : string = "id";
  private reverse : boolean;
  private cacheData : TableRow[] = [];
  private filteredData : TableRow[] = [];
  private pageData : TableRow[] = [];
  private showData : any[] = [];
  private selectedElement : TableRow;
  private filters : any = null;

  constructor(private tableService : TestTableService) { }

  @ViewChild(MatPaginator) paginator: MatPaginator;

  ngOnInit(){
    this.tableService.getData()
      .subscribe(table => this.cacheData = table);
  }

  ngAfterViewInit() {
    this.clearFilters();
  }

  clearFilters() : void{
    this.filters = {
      id : undefined,
      name : ""
    }
    this.applyFilter();
  }

  applyFilter() : void{
    this.filteredData = this.cacheData.slice();
    //do filtering
    this.paginator.length = this.filteredData.length;
    this.applyOrder();
  }

  applyOrder() : void{
    this.filteredData.sort(this.orderRule(this.order,this.reverse))
    this.applyPaging();
  }

  applyPaging() : void{
    this.pageData = this.filteredData.slice(this.paginator.pageIndex*this.paginator.pageSize,(this.paginator.pageIndex+1)*this.paginator.pageSize);
    this.applySelected();
  }

  applySelected() : void{
    this.showData = [];
    this.pageData.forEach(element => element==this.selectedElement ? this.showData.push(element,{detailRow:true,element:element}) : this.showData.push(element))
  }

  orderBy(order : string) : void{
    if(this.order == order){
      this.reverse = !this.reverse
    }
    else{
      this.order = order;
      this.reverse = false;
    }

    this.applyOrder();
  }

  expand(element : TableRow) : void{
    this.selectedElement = this.selectedElement == element ? null : element;
    this.applySelected();
  }

  orderRule(onderIndex,reverse){
    return function(a,b){
      if(a[onderIndex] > b[onderIndex]){
        return reverse ? -1 : 1;
      }
      if(a[onderIndex] < b[onderIndex]){
        return reverse ? 1 : -1;
      }
      return 0;
    }
  }

我已经向 the example stackblitz 添加了一个组件,不知道为什么 @ViewChild return 没有为分页器定义,但在本地它确实按预期工作