Search code examples
angulartypescriptangular-materialmat-table

How to use a slide toggle in a mat-table?


I have a mat-table that is viewed by 3 users of different roles. The table has multiple columns; one of the columns is active (a boolean value that controls the activation and deactivation of the account). I want to toggle that value with a switch within the same table (in another column), but only if the user is an admin.

I already achieved this using bootstrap table this way:

<table class="table table-striped">
    <thead>
        <tr>
            <th *ngIf="isAdmin(currentUser)" (click)="sortByAccountStatus()">
                <a>Deactivate/Activate</a>
                <a class="sort-by"></a>
            </th>
        </tr>
    </thead>
    <tbody>
        <tr *ngFor="let chartOfAccounts of accountList.reverse();" [hidden]="currentUser.role !== '1'">
            <td [hidden]="!chartOfAccounts.accountActive">
                <a (click)="deactivateAccount(chartOfAccounts)" class="text-danger">Deactivate</a>
            </td>
            <td [hidden]="chartOfAccounts.accountActive">
                <a (click)="activateAccount(chartOfAccounts)" class="text-danger">Activate</a>
            </td>
        </tr>
    </tbody>

    <tbody>
        <tr *ngFor="let chartOfAccounts of accountList" [hidden]="currentUser.role === '1'">
            <td>{{accountActiveStatus(chartOfAccounts.accountActive)}}</td>
        </tr>
    </tbody>
</table>

The code above will add a column to the table where the admin can click on activate or deactivate depending on the value of the boolean. The operations are implemented in the component.

Using mat-table, I got the boolean active column to work.

<ng-container matColumnDef="accountActive">
    <mat-header-cell *matHeaderCellDef mat-sort-header>Active</mat-header-cell>
    <mat-cell *matCellDef="let chartOfAccounts"> {{chartOfAccounts.accountActive}} </mat-cell>
</ng-container>

My goal is to create another column that holds the switch back and forth from true to false like this

<ng-container matColumnDef="activation">
    <mat-header-cell *matHeaderCellDef mat-sort-header>Activate/Deactivate</mat-header-cell>
    <mat-cell *matCellDef="let chartOfAccounts" [hidden]="!chartOfAccounts.accountActive">
        <a (click)="deactivateAccount(chartOfAccounts)" class="text-danger">Deactivate</a>
    </mat-cell>
    <mat-cell *matCellDef="let chartOfAccounts" [hidden]="chartOfAccounts.accountActive">
          <a (click)="activateAccount(chartOfAccounts)" class="text-danger">Deactivate</a>
    </mat-cell>
</ng-container>

This is not working and produces a weird result where when the toggle is switched off it cannot be switched back on again, the link disappears, and the toggle column too. Here is a screenshot.

screenshot

The boolean values are pushed to the right of the tables whenever they're switched to false

Updated:

Here is a stackblitz example. The toggle does not work but basically when you click on toggle the value of active should switch between true and false depending on if the toggle is on or off.


Solution

  • The reason your code isn't working is that you can't use two mat-cell in a mat-column. The second one will be ignored completely. This is why your table gets all messed up because on some cells the hidden is hiding the only cell present in that column (since the second cell is out of the picture).

    You shouldn't be using two separate cells for this. You want Activate/Deactivate to appear in the same cell. So instead of using hidden on the mat-cell, use ngIf on the anchor tags to conditionally show Activate/Deactivate.

    <ng-container matColumnDef="activation">
        <mat-header-cell *matHeaderCellDef mat-sort-header>Activate/Deactivate</mat-header-cell>
        <mat-cell *matCellDef="let chartOfAccounts">
            <a *ngIf="chartOfAccounts.accountActive" (click)="deactivateAccount(chartOfAccounts)" class="text-danger">Deactivate</a>
            <a *ngIf="!chartOfAccounts.accountActive" (click)="activateAccount(chartOfAccounts)" class="text-danger">Activate</a>
        </mat-cell>
    </ng-container>
    

    Edit: (after comments)

    If you don't want to show a particular column, just remove it from the displayed columns, this way it won't be rendered by mat-table.

    allColumns: string[] = ['position', 'name', 'weight', 'symbol', 'activate', 'toggle'];
    
    checkIfAdmin() {
        this.displayedColumns = this.isAdmin ? this.allColumns : this.allColumns.filter(column => column !== 'toggle');
    }
    

    Here is a working example on StackBlitz.