74

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 and displayedColumns 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 display value of element that has disCols's value , all cells are empty like that:

enter image description here

Other example that using {{element}} only:

enter image description here


Also as you can see:

  1. 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'];
  2. 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 ?

0

4 Answers 4

80

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:)

Sign up to request clarification or add additional context in comments.

7 Comments

Can you please send me full sample code to create dynamic column from API and fill grid
@Uttam I've added a full example below
Thanks for pointing out the clean, simple answer. I was bracing myself for another deep dive into "angularity". :--)
@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.
@srikanth to add onto the columns you'll want displayedColumns.push("newCol")
|
17

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[colIndex]}}</td>
  </ng-container>


  <tr mat-header-row *matHeaderRowDef="(columns$ | async)"></tr>
  <tr mat-row *matRowDef="let row; columns: (columns$ | async)"></tr>
</table>

7 Comments

can 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.
@ChandreshMishra I don't see why not, give it ago!
but where and how to apply the css class as tr and td will be generated by ngFor?
Add 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.
@CharlieReitzel Could you please elaborate a bit? Have a similar requirement.
|
14

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>

3 Comments

This 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>
How would you add other cell definitions, like buttons, checkboxes, selects, etc.?
Superb implementation. Thanks @R.J. Steinert
0

Dynamically render datatable from source (input type='checkbox')

I have a needs to render the column dynamically, I also expect to render cell value as checkbox. My code was edited from Angular example table with filtering and got the dynamic rendering part from @Jack. Hopefully code attached below can provide a brief idea to junior like me.

What I did was

  1. wrap <td mat-cell... </td> with <div *ngIf="condition"></div>
<div *ngIf="disCol === 'username'">
    <td mat-cell *matCellDef="let element"> {{element[disCol]}}</td>
</div>
<div *ngIf="disCol !== 'username'">
    <td mat-cell *matCellDef="let element">
        <input *ngIf="element[disCol] == 'Yes'" type="checkbox" />
        <input *ngIf="element[disCol] == 'No'" type="checkbox" checked/>
    </td>
</div>
  1. It is also worth mentioning that I am on Angular 16, the import statement is slightly different with older version. Older version basically import directly from '@angular/material'
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatRippleModule } from '@angular/material/core';
import { MatTableModule } from '@angular/material/table';

dynamic-tb.component.html

<div class="example-container mat-elevation-z8">

  <mat-table #table [dataSource]="dataSource">
    <ng-container *ngFor="let disCol of displayedColumns; let colIndex = index" matColumnDef="{{disCol}}">
      <th mat-header-cell *matHeaderCellDef>{{disCol}}</th>
      <div *ngIf="disCol === 'username'">
        <td mat-cell *matCellDef="let element"> {{element[disCol] == undefined ? "-NA-" : element[disCol]}}</td>
      </div>
      <div *ngIf="disCol !== 'username'">
        <td mat-cell *matCellDef="let element">
          <input *ngIf="element[disCol] == 'Yes'" type="checkbox" />
          <input *ngIf="element[disCol] == 'No'" type="checkbox" checked/>
        </td>
      </div>
    </ng-container>"

    <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
    <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
  </mat-table>
</div>

dynamic-tb.component.ts

import { Component, OnInit } from '@angular/core';
import { MatTableDataSource } from '@angular/material/table';

@Component({
  selector: 'app-dynamic-tb',
  templateUrl: './dynamic-tb.component.html',
  styleUrls: ['./dynamic-tb.component.css']
})
export class DynamicTbComponent implements OnInit {

  displayedColumns: string[] = [];
  dataSource: any;
  ELEMENT_DATA: any[] | undefined;

  ngOnInit(): void {
    this.displayedColumns = ['username', 'feature_1', 'feature_2', 'feature_3'];
    this.ELEMENT_DATA = [
      { username: "Jonah", feature_1: 'Yes', feature_2: 'No', feature_3: 'No' },
      { username: "Alan", feature_1: 'Yes', feature_2: 'No', feature_3: 'Yes' },
      { username: "John", feature_1: 'No', feature_2: 'Yes', feature_3: 'No' },
      { username: "Ann", feature_1: 'Yes', feature_2: 'No', feature_3: 'Yes' },
      { username: "Lee", feature_1: 'No', feature_2: 'No', feature_3: 'No' },
      { username: "Royce", feature_1: 'Yes', feature_2: 'Yes', feature_3: 'No' },
      { username: "Suzzy", feature_1: 'No', feature_2: 'No', feature_3: 'No' },
    ];
    this.dataSource = new MatTableDataSource(this.ELEMENT_DATA);
  }

}

dynamic-tb.module.ts

import { NgModule } from '@angular/core';

import { DynamicTbComponent } from './profiles.component';
import { CommonModule } from '@angular/common';

import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatRippleModule } from '@angular/material/core';
import { MatTableModule } from '@angular/material/table';

@
NgModule
({
  declarations: [
    DynamicTbComponent
  ],
  imports: [
    CommonModule,
    MatButtonModule,
    MatFormFieldModule,
    MatInputModule,
    MatRippleModule,
    MatTableModule,
  ],
  exports: [
    DynamicTbComponent
  ]
})

export class DynamicTbModule {}

dynamic-tb.component.css

/* Structure */
.example-container {
  display: flex;
  flex-direction: column;
  min-width: 300px;
}

.example-header {
  min-height: 64px;
  padding: 8px 24px 0;
}

.mat-form-field {
  font-size: 14px;
  width: 100%;
}

.mat-table {
  overflow: auto;
  max-height: 500px;
}

/**
  Added by me
*/
th, td {
  width: 100px;
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.