Expandable Rows in Mat-Table programmed with Angular

With Material package using Mat-Table, it is rather easy to program tables with expandable rows. We have used Expandable Rows in Mat-Tables when we wanted to provide extra information about the data in the row.

Mat-Table without Expandable Rows

We first look at how to create a Table which shows only one type of Data. Provided below is an example.

Mat-Table without Expanded Rows

The HTML Code to program the above table is provided below.

    <div class="mat-elevation-z8">
        <table mat-table [dataSource]="listData" class="mat-elevation-z8" matSort width="100%">

            <!--- Note that these columns can be defined in any order.
                    The actual rendered columns are set as a property on the row definition" -->

            <!-- Currency Code Column -->
            <ng-container matColumnDef="currencyCode">
                <th mat-header-cell *matHeaderCellDef mat-sort-header> Currency Code </th>
                <td mat-cell *matCellDef="let element" align="left"> {{element.currencyCode}} </td>
            </ng-container>

            <!-- Currency Symbol Column -->
            <ng-container matColumnDef="currencySymbol">
                <th mat-header-cell *matHeaderCellDef mat-sort-header> Symbol </th>
                <td mat-cell *matCellDef="let element" align="left"> {{element.currencySymbol}} </td>
            </ng-container>

            <!-- Base Name Column -->
            <ng-container matColumnDef="currencyBaseName">
                <th mat-header-cell *matHeaderCellDef mat-sort-header> Base Name </th>
                <td mat-cell *matCellDef="let element" align="left"> {{element.currencyBaseName}} </td>
            </ng-container>

            <!-- Fraction Symbol Column -->
            <ng-container matColumnDef="fractionSymbol">
                <th mat-header-cell *matHeaderCellDef align="center"> Fraction Symbol </th>
                <td mat-cell *matCellDef="let element" align="center"> {{element.fractionSymbol}} </td>
            </ng-container>

            <!-- Fraction Name Column -->
            <ng-container matColumnDef="fractionName">
                <th mat-header-cell *matHeaderCellDef> Fraction Name </th>
                <td mat-cell *matCellDef="let element" align="left"> {{element.fractionName}} </td>
            </ng-container>

            <!-- Edit Column -->
            <ng-container matColumnDef="edit">
              <th mat-header-cell *matHeaderCellDef>
              </th>
              <td mat-cell *matCellDef="let row">
                  <a mat-button matTooltip="Edit" [routerLink]="['/currency/edit', row.currencyCode]" *ngIf="isUserAuthenticated"><mat-icon>create</mat-icon></a>
              </td>
            </ng-container>

            <!-- Delete Column -->
            <ng-container matColumnDef="delete">
              <th mat-header-cell *matHeaderCellDef>
              </th>
              <td mat-cell *matCellDef="let row">
                  <button mat-button matTooltip="Delete" color="warn" (click)="onDelete(row.currencyCode)" *ngIf="isUserAuthenticated"><mat-icon>clear</mat-icon></button>
              </td>
            </ng-container>

            <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
            <tr mat-row *matRowDef="let row; columns: displayedColumns;" (click)="selectRow(row)" [ngClass]="{ 'selected': row === selectedRow }"></tr>
        </table>

        <mat-paginator [pageSizeOptions]="[7, 15, 20]" showFirstLastButtons></mat-paginator>
    </div>

Steps to create Tables with Expandable Rows

Now, we create a Mat-Table with Expandable Rows.

Codes in the .HTML File

The first step is to declare that the Mat-Table has Multiple Row definitions. This can be accomplished by adding the multiTemplateDataRows directive in the table tag.

<table id="DataPreview" mat-table [dataSource]="uploadData" multiTemplateDataRows class="mat-elevation-z8" width="100%">

Next, add the row definitions for the parent row and the child row. The code below shows how this can be done.

<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"
                                      class="example-element-row"
                                      [class.example-expanded-row]="expandedElement === row"
                                      (click)="expandedElement = row"
                                  >
</tr>
<tr mat-row *matRowDef="let row; columns: ['expandedDetail']" class="example-detail-row"></tr>

Now, include the section that you would like to add in the Expanded Row Section. The code below shows an example.

<ng-container matColumnDef="expandedDetail">
   <td mat-cell *matCellDef="let element" [attr.colspan]="displayedColumns.length">
      <div class="example-element-detail"
           [@detailExpand]="element == expandedElement ? 'expanded' : 'collapsed'" *ngIf="element.recordStatus === I_INVALID_RECORD_INDICATOR">
            <table width="100%">
                  <tr>
                     <td align="left">Original Record: <b>{{element.record}}</b></td>
                 </tr>
                 <tr>
                     <td align="left"><p class="FailedMessage">Error: <b>{{element.recordError}}</b></p></td>
                 </tr>
          </table>
      </div>
   </td>
</ng-container>

Code in the .TS File

Add the code to add the animation support for the Expanded Rows display. Remember to include all the libraries.

import { animate, state, style, transition, trigger } from '@angular/animations';
@Component({
    selector: 'app-country-upload',
    styleUrls: ['../../general/general-upload.component.css', './country-upload.component.css'],
    templateUrl: './country-upload.component.html',
    animations: [
        trigger('detailExpand', [
        state('collapsed', style({height: '0px', minHeight: '0', display: 'none'})),
        state('expanded', style({height: '*'})),
        transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
      ]),
    ],
})

Connect the variable holding the data for the Expanded Rows.

expandedElement: UploadData;

Code in the .CSS File

For this example, the following .CSS file will be required for the rendering to happen properly. This .CSS file is provided in the Material documentation. This can be changed according to needs.

table {
  width: 100%;
}

tr.example-detail-row {
  height: 0;
}

tr.example-element-row:not(.example-expanded-row):hover {
  background: #f5f5f5;
}

tr.example-element-row:not(.example-expanded-row):active {
  background: #efefef;
}

.example-element-row td {
  border-bottom-width: 0;
}

.example-element-detail {
  overflow: hidden;
  display: flex;
}

.example-element-diagram {
  min-width: 80px;
  border: 2px solid black;
  padding: 8px;
  font-weight: lighter;
  margin: 8px 0;
  height: 104px;
}

.example-element-symbol {
  font-weight: bold;
  font-size: 40px;
  line-height: normal;
}

.example-element-description {
  padding: 16px;
}

.example-element-description-attribution {
  opacity: 0.5;
}

Example Output

The above code will produce the following output.

Mat-Table with Expanded Rows

The video below demonstrates how it works.

Adding a Mat-Table as an Expandable Row

Any set of HTML objects can be added in the Expandable Row. Here is an example of how we added a Mat-Table in the Expandable Row.

      <ng-container matColumnDef="expandedDetail">
        <td mat-cell *matCellDef="let element" [attr.colspan]="displayedColumns.length">
          <div class="example-element-detail"
              [@detailExpand]="element == expandedElement ? 'expanded' : 'collapsed'" *ngIf="element.taxVersion.length > 0">

            <table mat-table [dataSource]="element.taxVersion" class="mat-elevation-z8" width="100%">

              <ng-container matColumnDef="taxID">
                <th mat-header-cell *matHeaderCellDef> Tax ID </th>
                <td mat-cell *matCellDef="let elem" align="center"> {{elem.taxID}} </td>
              </ng-container>
            
              <ng-container matColumnDef="versionCode">
                <th mat-header-cell *matHeaderCellDef> Version Code </th>
                <td mat-cell *matCellDef="let elem" align="center"> {{elem.versionCode}} </td>
              </ng-container>
            
              <ng-container matColumnDef="versionDate">
                <th mat-header-cell *matHeaderCellDef> Valid From </th>
                <td mat-cell *matCellDef="let elem" align="center"> {{elem.versionDate | date:'dd-MMM-yyyy'}} </td>
              </ng-container>
            
              <ng-container matColumnDef="taxRate">
                <th mat-header-cell *matHeaderCellDef> Tax Rate </th>
                <td mat-cell *matCellDef="let elem" align="center"> {{elem.taxRate/100 | percent:'1.2-2'}} </td>
              </ng-container>

                
              <!-- Edit Tax Version Column -->
              <ng-container matColumnDef="editVersion">
                  <th mat-header-cell *matHeaderCellDef>
                  </th>
                  <td mat-cell *matCellDef="let row" width="10%">
                      <a mat-button matTooltip="Edit" [routerLink]="['/tax/edit', row.taxCode]" *ngIf="isUserAuthenticated"><mat-icon>create</mat-icon></a>
                  </td>
                </ng-container>
          
                <!-- Delete Tax Version Column -->
                <ng-container matColumnDef="deleteVersion">
                  <th mat-header-cell *matHeaderCellDef>
                  </th>
                  <td mat-cell *matCellDef="let row" width="10%">
                      <button mat-button matTooltip="Delete" color="warn" (click)="onDelete(row.taxCode)" *ngIf="isUserAuthenticated"><mat-icon>clear</mat-icon></button>
                  </td>
                </ng-container>
            
              <tr mat-header-row *matHeaderRowDef="displayedColumnsVersion"></tr>
              <tr mat-row *matRowDef="let row; columns: displayedColumnsVersion;"></tr>
            </table>
          </div>
        </td>
      </ng-container>

 

This video demonstrates how it work.

Thank you for your kind attention!!!

Advertisements

This site uses Akismet to reduce spam. Learn how your comment data is processed.