13

I am building a table in Angular8 with Material table.

I am using an array of strings to define the displayedColumns and passing in each array value to the matColumnDef directive.

This is working properly.

TS

displayedColumns: any[] = ['username', 'firstname', 'lastname'];

HTML

<div class="table-container mat-elevation-z8">
  <table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
    <ng-container *ngFor="let column of displayedColumns;" [matColumnDef]="column">
      <th mat-header-cell *matHeaderCellDef> {{ column }} </th>
      <td mat-cell *matCellDef="let element"> {{ element[column] }} </td>
    </ng-container>
    <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
    <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
  </table>
</div>

Question: Is it possible to define the displayedColumns as an array of objects rather than an array of strings?

I want to set a different value for the column display value, and also possibly set some other column properties conditionally.

Example: Notice the displayedColumns is an array of objects. This does NOT work.

TS

  displayedColumns: any[] = [
    { name: 'username', display: 'User Name' },
    { name: 'firstname', display: 'First Name' },
    { name: 'lastname', display: 'Last Name' }
  ];

HTML

<div class="table-container mat-elevation-z8">
  <table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
    <ng-container *ngFor="let column of displayedColumns;" [matColumnDef]="column.name">
      <th mat-header-cell *matHeaderCellDef> {{ column.display }} </th>
      <td mat-cell *matCellDef="let element"> {{ element[column.name] }} </td>
    </ng-container>
    <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
    <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
  </table>
</div>

Error message:

Could not find column with id "[object Object]".
    at getTableUnknownColumnError (table.js:890)
    at table.js:1973

I think the error indicates that the mat-table is iterating the displayedColumns, expecting an array element, but getting an object. Is there a way to fix this or another way to achieve this requirement?

pengz
  • 2,279
  • 3
  • 48
  • 91
  • 1
    Is this what you are looking [for](https://material.angular.io/components/table/api#MatTextColumn)? – KiraAG Jun 20 '19 at 03:01

1 Answers1

18

I was able to achieve this by creating a separate object to store the table column name and display name.

  // Table columns
  initColumns: any[] = [
    {
      name: 'username',
      display: 'User Name'
    },
    {
      name: 'firstname',
      display: 'First Name'
    },
    {
      name: 'Description',
      display: 'Description'
    }
  ];

The displayedColumns is set to an array of strings for each column 'name' property.

  // Displayed columns array of strings
  displayedColumns: any[] = this.initColumns.map(col => col.name);

The ngFor iterates on the initColumns array of objects and sets the matColumnDef, shows the display name and row values.

<ng-container [matColumnDef]="column.name" *ngFor="let column of initColumns;">
<th mat-header-cell *matHeaderCellDef> {{column.display}}</th>
<td mat-cell *matCellDef="let element"> {{element[column.name]}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>

I got the idea from an answer to a similar question. Note that the trackbyindex part was not required for my particular solution.

pengz
  • 2,279
  • 3
  • 48
  • 91
  • 1
    these two do the trick, so we need two columns' variables - like `matColumns` (aka `displayedColumns`), and `columns` (like array of objects) for using inside the table rows any properties you want. – Igor Kurkov Dec 05 '19 at 11:42
  • 1
    Great! This helped so much with a mat table data source I was trying to configure. Basically I had a student's name as the left-most column header, and a dynamic amount of Dates as the rest of the columns. In the cells of those Date columns I needed a grade, which was on the object itself I passed in, but under a different property name. Following the initColumns/displayColumns worked perfectly in this situation. Thanks! – necampanini Feb 13 '21 at 23:33
  • `[matColumnDef]="column.name"` should be `[matColumnDef]="{{column.name}}"` – vk-code Feb 07 '22 at 14:47