Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MatTable Expand Collapse Icon issue on pagination and sort

I've a angular material table which uses detailRow directive to insert a detail/sibling adjacent row to a table row.

StackBlitz

I wanted to give it an appearance of as if the row is being expanded or collapsed, so I added couple of icons to it which are toggled on the click of cell containing them.

<mat-header-cell *matHeaderCellDef> Action </mat-header-cell>
      <mat-cell *matCellDef="let element"> 
         <button mat-icon-button color="primary"  (click)="element[i] = !element[i]">
            <mat-icon id="expand_more"  #expand_more *ngIf="!element[i] "  >expand_more</mat-icon>
            <mat-icon id="expand_less"  #expand_less *ngIf="element[i] ">expand_less</mat-icon>
          </button> 
      </mat-cell>

However if the I leave the row expanded and paginate or do a sort the icons do not toggle because there's no way for them to be toggled.

I've tried hooking into the page event or the sortChange event but came up empty.

I'm aware that there's new way to do expand/collapse in angular material v7 which probably works well with pagination and sort but its gonna be a while before I upgrade, in the mean time does anyone have any ideas on how to solve this.

like image 915
kal93 Avatar asked Oct 16 '22 09:10

kal93


1 Answers

Short Answer

In cdk-detail-row.directive.ts add this

  ngOnDestroy(): void {
    this.row[undefined] = false;
  }

Long Answer

Firstly, You are capturing click in 2 places once in mat-row and the other in mat-cell(Clicking on the icon triggers both events. Clicking anywhere else on the row only triggers onToggleChange). And also this element[i] = !element[i] is a hack - (variable i is undefined). So if you click anywhere else in the row the expand icon does not change this is why I got confused as I thought it is not suppose to change. The example will just take out the click on mat-cell to make it simple.

In table-basic-example.html you should remove the (click) output from it and add the row argument to the method onToggleChange($event, row). And change the *ng-if to listen to element.close instead

<ng-container matColumnDef="expandCollapse">
  <mat-header-cell *matHeaderCellDef> Action </mat-header-cell>
  <mat-cell *matCellDef="let element"> 
     <button mat-icon-button color="primary">
        <mat-icon id="expand_more"  #expand_more *ngIf="!element.close"  >expand_more</mat-icon>
        <mat-icon id="expand_less"  #expand_less *ngIf="element.close">expand_less</mat-icon>
      </button> 
  </mat-cell>
</ng-container>

<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns;"
        class="element-row"
        [cdkDetailRow]="row" [cdkDetailRowTpl]="tpl"
        (toggleChange)="onToggleChange($event, row)">
</mat-row>

table-basic-example.ts

Add the close property to interface element

export interface Element {
    name: string;
    position: number;
    weight: number;
    symbol: string;
    close?: boolean;
}

Now we will handle the close and open of the row in the method onToggleChange.

onToggleChange(cdkDetailRow: CdkDetailRowDirective, row: Element): void {
    if (this.singleChildRowDetail && this.openedRow && this.openedRow.expended) {
        this.openedRow.toggle();
    }
    if (!row.close) {
        row.close = true;
    } else {
        row.close = false;
    }
    this.openedRow = cdkDetailRow.expended ? cdkDetailRow : undefined;
}

Lastly, In cdk-detail-row.directive.ts we will want to close the row once the directive is destroyed by pagination or toggling away. So we will implement the onDestroy method

export class CdkDetailRowDirective implements OnDestroy{
     ...Details of implementation.....
}

The new ngOnDestroy method should look like this

ngOnDestroy(): void {
  this.row.close = false;
}
like image 197
chronolegend Avatar answered Oct 23 '22 03:10

chronolegend