简体   繁体   中英

why sorting doesn't work with material table wrapped within accordions?

As per requirement, I have two accordions, each with one independent table. By clicking on the first accordion it opens and calls API, fetches the data from the backend and renders on the table within the 1st accordion. With this table, sorting works fine but when I open the second accordion sorting doesn't work.

HTML CODE:

<mat-accordion multi="false" [togglePosition]="'before'">
    <ng-container *ngFor="let item of usersData; let i=index;last as last">
    <mat-expansion-panel class="mt-2" #isExpanded>
        <mat-expansion-panel-header (click)="showData(item.id, isExpanded.expanded)">
            <table>
                <tr>
                    <th class="col-4 px-2">Number</th>
                    <th class="col-4">Name</th>
                    <th class="col-4">Date</th>
                    <td> <button mat-icon-button matTooltip="Delete">
                            <mat-icon class="mat-icon mat-primary">delete</mat-icon>
                        </button>
                    </td>
                </tr>
                <tr>
                    <td class="col-4 px-2"><span matTooltip="{{item.number}}">{{item.number}}</span></td>
                    <td class="col-4"><span matTooltip="{{item.name}}">{{item.name}}</span></td>
                    <td class="col-4"><span matTooltip="{{item.createdAt | date: 'yyyy-MM-dd'}}">{{item.createdAt | date: 'yyyy-MM-dd'}}</span></td>
                    <td></td>
                </tr>
            </table>
        </mat-expansion-panel-header>
        <div class="m-2" fxLayout="row wrap" fxLayoutGap="20px">
            <div class="bh-table-title bh-table mt-2 scroll">
                <table mat-table *ngIf="dataSource.data.length > 0" [dataSource]="dataSource" matSort #MatSorteds matSortStart="desc">
                    <ng-container matColumnDef="number">
                        <th mat-header-cell *matHeaderCellDef mat-sort-header>Number</th>
                        <td mat-cell *matCellDef="let element" matTooltip="{{element.number}}">
                            <button class="link" (click)="navigate(element)">{{element.number}}</button>
                        </td>
                    </ng-container>
                    <ng-container matColumnDef="Name">
                        <th mat-header-cell *matHeaderCellDef mat-sort-header> Name</th>
                        <td mat-cell *matCellDef="let element" matTooltip="{{element.Name}}">
                            {{element.name}}</td>
                    </ng-container>
                    <ng-container matColumnDef="createdAt">
                        <th mat-header-cell *matHeaderCellDef mat-sort-header>Date</th>
                        <td mat-cell *matCellDef="let element" matTooltip="{{element.createdAt| date: 'yyyy-MM-dd'}}">
                            {{element.createdAt | date: 'yyyy-MM-dd'}}</td>
                    </ng-container>
                    <tr mat-header-row *matHeaderRowDef="columnsToDisplay;sticky: true" mat-sort></tr>
                    <tr mat-row *matRowDef="let row; columns: columnsToDisplay;"></tr>
                </table>
            </div>
        </div>
        <div class="no-records-panel" *ngIf="dataSource.data.length === 0">No Records found</div>
    </mat-expansion-panel>
    </ng-container>
</mat-accordion>

COMPONENT CODE:

 @ViewChild(MatSort) matSort!: MatSort;

 ngAfterViewInit(): void {
    this.dataSource.data = this.data;
    this.dataSource.paginator = this.paginator;
     this.dataSource.sort = this.matSort;
  }

You have an unique accordion open at time, so the only is "give time" to Angular to show the data.table before asig the mat-sort.

Instead of use ngAfterViewInit, it's when you get the data were you need asign

I imagine you have a function showData

showData(id:number, isExpanded:bool)
{
    this.myservice.getData(id).subscribe(res=>{
       this.dataSources=new MatDataTable(res)
       //enclosed in a setTimeout()
       setTimeout(()=>{
          this.dataSource.sort=this.matSort
       })
    })
}

This work because ViewChild get the "first" MatSort that find.

Another way to see is use ViewChildren instead ViewChild

@ViewChildren(MatSort) matSorts: QueryList<MatSort>;

Then, always all was "visibles" (not under a *ngIf) can be sure that the Querylist element in first place is the first Math-sort for the first table.

So first we need "remove" the *ngIf

<table mat-table *ngIf="dataSource.data.length > 0"... matSort>
  ..
</table>

And define an array of dataSources

dataSource:MatDataSource[]=[]

So, becomes like

<table mat-table *ngIf="dataSource[i].data.length > 0"... matSort>.. </table>
<div [style.visibility]="dataSource[i].data.length <= 0:'collapse':null">
  <table mat-table ... matSort>
    ..
  </table>
</div>

Now, when give value to the dataSource.sort

we can do

this.usersData.forEach((_,index:number)=>{
    this.dataSource[index]new MatDataSource() //a matDataSource empty
    this.dataSource[index].sort=this.matSorts.find((x:any,index:number)=>index==i)
})

Then give value to our MathDataSource

showData(id:number, isExpanded:bool)
{
    const index=this.usersData.findIndex(x=>x.id==id);

    this.myservice.getData(id).subscribe(res=>{
       this.dataSources[index].data=res
    })
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM