The easiest way to do this is to include all possible columns in an array (definedColumns), and iterate over it for the header & cell definitions.
Then for the columns you want to show conditionally, you can control which are displayed by having a separate array that contains a string list of the columns you want to actually show (columnsToDisplay). Simply adding and removing columnIds from this list will result in the table updating with the appropriate columns.
<table mat-table [dataSource]="data" class="mat-elevation-z8">
<ng-container matColumnDef="{{column}}" *ngFor="let column of definedColumns">
<th mat-header-cell *matHeaderCellDef> {{column}} </th>
<td mat-cell *matCellDef="let element"> {{element[column]}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="columnsToDisplay"></tr>
<tr mat-row *matRowDef="let row; columns: columnsToDisplay;"></tr>
</table>
There's a live example of this given on the angular material website, see "Table dynamically changing the columns displayed
" on their examples page here - https://material.angular.io/components/table/examples
Note: For more advanced examples, like if you needed to pass a TemplateRef for your cell and header definitions, you'll probably want to check out Angular Material's CDK.
You can dynamically add and remove columns from the table using Angular's CDK API. If you add #table to your mat-table element then you can have your component use it as a @ViewChild. Then you can use this.table.addColumnDef and pass your new column. See https://material.angular.io/cdk/table/api#CdkColumnDef for more details.