Angular Material Table Dynamic Columns without model
Solution 1
I found solution :) It is very very easy but i could't see at first :) only like that :
<mat-cell *matCellDef="let element "> {{element[disCol]}} </mat-cell>
I must use {{element[disCol]}}
only in HTML.
Now , everything is ok:)
Solution 2
For a full working example based on @mevaka's
Where jobDetails$
is the array of items.
columns$
is equvilent to Object.keys(jobDetails$[0])
so is just a string[]
<table mat-table [dataSource]="jobDetails$ | async">
<ng-container *ngFor="let disCol of (columns$ | async); let colIndex = index" matColumnDef="{{disCol}}">
<th mat-header-cell *matHeaderCellDef>{{disCol}}</th>
<td mat-cell *matCellDef="let element">{{element[disCol]}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="(columns$ | async)"></tr>
<tr mat-row *matRowDef="let row; columns: (columns$ | async)"></tr>
</table>
Solution 3
I've tried my best to boil a dynamic table down to the minimum. This example will display any columns given an array of flat objects with any keys. Note how the first object has an extra "foo" property that causes an entire column to be created. The DATA const could be some data you get from a service. Also, you could add a "column ID -> label" mapping into this if you know some common property names you'll be getting the JSON. See the stachblitz here.
import {Component, ViewChild, OnInit} from '@angular/core';
const DATA: any[] = [
{position: 1, name: 'sdd', weight: 1.0079, symbol: 'H', foo: 'bar'},
{position: 2, name: 'Helium', weight: 4.0026, symbol: 'He'},
{position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li'},
{position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be'},
{position: 5, name: 'Boron', weight: 10.811, symbol: 'B'},
{position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C'}
];
@Component({
selector: 'dynamic-table-example',
styleUrls: ['dynamic-table-example.css'],
templateUrl: 'dynamic-table-example.html',
})
export class DynamicTableExample implements OnInit {
columns:Array<any>
displayedColumns:Array<any>
dataSource:any
ngOnInit() {
// Get list of columns by gathering unique keys of objects found in DATA.
const columns = DATA
.reduce((columns, row) => {
return [...columns, ...Object.keys(row)]
}, [])
.reduce((columns, column) => {
return columns.includes(column)
? columns
: [...columns, column]
}, [])
// Describe the columns for <mat-table>.
this.columns = columns.map(column => {
return {
columnDef: column,
header: column,
cell: (element: any) => `${element[column] ? element[column] : ``}`
}
})
this.displayedColumns = this.columns.map(c => c.columnDef);
// Set the dataSource for <mat-table>.
this.dataSource = DATA
}
}
<mat-table #table [dataSource]="dataSource">
<ng-container *ngFor="let column of columns" [cdkColumnDef]="column.columnDef">
<mat-header-cell *cdkHeaderCellDef>{{ column.header }}</mat-header-cell>
<mat-cell *cdkCellDef="let row">{{ column.cell(row) }}</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
</mat-table>
mevaka
Updated on August 19, 2021Comments
-
mevaka almost 3 years
I need to use angular material table without model, because I don't know what will come from service.
So I am initializing my
MatTableDataSource
anddisplayedColumns
dynamically in component like that :TableComponent :
ngOnInit() { this.vzfPuanTablo = [] //TABLE DATASOURCE //GET SOMETHING FROM SERVICE this.listecidenKisi = this.listeciServis.listecidenKisi; this.listecidenVazife = this.listeciServis.listecidenVazife; //FILL TABLE DATASOURCE var obj = {}; for (let i in this.listecidenKisi ){ for( let v of this.listecidenVazife[i].vazifeSonuclar){ obj[v.name] = v.value; } this.vzfPuanTablo.push(obj); obj={}; } //CREATE DISPLAYED COLUMNS DYNAMICALLY this.displayedColumns = []; for( let v in this.vzfPuanTablo[0]){ this.displayedColumns.push(v); } //INITIALIZE MatTableDataSource this.dataSource = new MatTableDataSource(this.vzfPuanTablo); }
The most important part of code is here :
for( let v in this.vzfPuanTablo[0]) { this.displayedColumns.push(v); }
I am creating
displayedColumns
here dynamically, it means; even I don't know what will come from service, I can show it in table.For example
displayedColumns
can be like that:- ["one", "two" , "three" , "four" , "five" ]
or
- ["stack","overflow","help","me]
But it is not problem because I can handle it.
But when I want to show it in HTML, I can't show properly because of
matCellDef
thing:TableHtml :
<mat-table #table [dataSource]="dataSource" class="mat-elevation-z8"> <ng-container *ngFor="let disCol of displayedColumns; let colIndex = index" matColumnDef="{{disCol}}"> <mat-header-cell *matHeaderCellDef>{{disCol}}</mat-header-cell> <mat-cell *matCellDef="let element "> {{element.disCol}} </mat-cell> </ng-container> <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row> <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row> </mat-table>
My problem is here:
<mat-cell *matCellDef="let element "> {{element.disCol}} < / mat-cell>
In fact, I want to display
element."disCol's value"
in the cell, but I don't know how can I do that.Otherwise, everything is ok except this
element."disCol's value"
thing.
When I use
{{element.disCol}}
to displayvalue of element that has disCols's value
, all cells are empty like that:Other example that using
{{element}}
only:
Also as you can see:
-
Table datasource is changing dynamically. It means I can't use
{{element.ColumnName}}
easily, because I don't know even what is it.- First Example's displayedColumns = ['Vazife', 'AdSoyad', 'Kirmizi', 'Mavi', 'Yesil', 'Sari'];
- Second Example's displayedColumns = ['Muhasebe', 'Ders', 'Egitim', 'Harici'];
-
matHeaderCellDef
is correct , because it is using {{disCol}} directly.
But I need to read disCol's value, and display
element.(disCol's value)
in the cell.How can I do that ?
-
Techiemanu about 5 yearsCan you please send me full sample code to create dynamic column from API and fill grid
-
Jack about 5 years@Uttam I've added a full example below
-
Chandresh Mishra over 4 yearscan we apply different column size using css if we have a dynamic table? I want to covert my static table to dynamic to support 2 different views.
-
Jack over 4 years@ChandreshMishra I don't see why not, give it ago!
-
Chandresh Mishra over 4 yearsbut where and how to apply the css class as tr and td will be generated by ngFor?
-
Charlie Reitzel over 4 yearsAdd a "data type" attribute to your abstract row model, which can be used to generate a CSS class for each data and header cell. E.g. numeric types should be right aligned.
-
Charlie Reitzel over 4 yearsThanks for pointing out the clean, simple answer. I was bracing myself for another deep dive into "angularity". :--)
-
srikanth over 4 years@mevaka, Could you please provide full code to create dynamic columns. I am facing the issue while adding the colums. I am adding the columns based on button click. i mean if 5 buttons are dere . by click on each button , columns will be adding and also rows are increasing. if i add 5 columns , 5 rows are coming, but i need only one row.
-
acandylevey almost 4 years@srikanth to add onto the columns you'll want
displayedColumns.push("newCol")
-
cody.codes about 3 yearsAs long as your array of objects' keys structure is consistent you can also use something like this to dynamically generate the columns:
this.displayedColumns = Object.keys(data[0]);
-
Chen Peleg over 2 yearsThis example really helped me to get started, and the closest to the documentation. Only difference is that now it's recommended to use mat-table "Directive": <td mat-cell *cdkCellDef="let row">{{ column.cell(row) }}</td>
-
Pinka over 2 years@mevaka my displayedColumns is an array of objects (it has id, label, etc) , so
element[disCol]
doesn't work for me, I tried withelement[disCol.id]
,element.id
andelement.disCol.id
, but no luck. Any thoughts? -
infinite almost 2 years@CharlieReitzel Could you please elaborate a bit? Have a similar requirement.
-
Charlie Reitzel almost 2 yearsIt's been a while ... but here goes: Instead of a simple list/array of strings, each "Column Definition" can be a data structure that gives name, display name, datatype, required/optional, etc. for each column. Each row and column value can refer to its column definition by column index (either a simple numeric array index or an associative array by column name). Thus, you can locate enough information to define CSS class names by datatype or any other information you keep in the column definition objects (1 per column) in every row of that column. Similar to virtual data in C++/MFC.
-
Charlie Reitzel almost 2 yearsHave a look at the answer from @rjsteinert, which illustrates what I'm trying to describe.