Catleen
Catleen

Reputation: 81

Is there a way to create an Angular dynamic material table on columns and not on rows, with a more complex object dataSource?

I want to build a table where every column is a response from an API call and contains some elements. I tried different approaches with material table but nothing seems to work. The problem comes from the fact that I try to build it on columns and not on rows (there are different number of elements on every col)

I've also tried with a simple table and it works but then the view doesn't look like it should. Every column is wrapped in an <ng-container> and it should be displayed as a new column, but it displays them on the same column. I've also tried an approach using divs and that works fine but then I can not make the header of the table to be sticky and fixed when I scroll down. (also, is not a good idea using divs inside a table) This is how my table looks now, being the best variant so far:

 <table class="mat-table">
      <ng-container *ngFor="let elem of selectedCheckValues">
        <th class="mat-header-cell">{{ elem.name }}</th>
        <tr id="{{element}}" *ngFor="let element of elem.items; let idx=index;" (click)="onRowClick(elem, element)">
          <td class="mat-cell">{{element}}</td>
        </tr>
      </ng-container>
    </table>

And this is how my data looks like:

export interface SelectedCheckValues {
  name: string;
  items: string[];
}

Where name should be the table header and items the elements from the column.

Upvotes: 1

Views: 2232

Answers (2)

Eliseo
Eliseo

Reputation: 57929

If you want to distribute a series of calls in columns in a mat-table you need "create" a DataSource with the columns.

I made a simple example. In this I imagine you get the data using a forkJoin so you can have something like

  dataSource:any[] = [];
  data=[0,1,2]
  headers:string[]=[]
  constructor(private dataService:DataService){}
  ngOnInit(){

    //we make a "typical" forkJoin where transform the "data" in an
    //array of "Observables" and use forkJoin
    forkJoin(this.data.map(x=>this.dataService.getData(x)))
    .subscribe((res:any[])=>{
       //when we has the response we "formed" the dataSource
       let i=0;
       let goon=true;
       this.headers=res.map(x=>x.name)
       while(goon)
       {
          const items=res.map(x=>x.items[i])
          goon=items.find(x=>x!==null)
          this.dataSource.push(items)
          i++;
       }
    })
  }

And a .html

<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">

  <ng-container *ngFor="let header of headers;let i=index" [matColumnDef]="header">
    <th mat-header-cell *matHeaderCellDef>{{header}}</th>
    <td mat-cell *matCellDef="let element"> {{element[i]}} </td>
  </ng-container>


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

See the stackblitz

BTW, mat table with a sticky it's only .css, so you can take as approach use a normal table and the .css to sticky the "header"

Update: it's difficult for me imagine how you get your data, if you get it using three calls to an api

getMFTNO() //return some like [101,102,103]
getDF_RESULT() //return some like ["DL","FM","FT"]
getPERSNO() //return some like [0,000000,0000001]

And all the apis return the same number of elements, the idea is use a forJoin of the three calls and map to an array of object

forkJoin([this.getMFTNO(),this.getDF_RESULT(),this.getPERSNO()])
      .pipe(
        map(([mftno,dfResult,persno]:[any[],any[],any[]])=>{
          return mftno.map((x,index)=>(
          { 
            mftno:x,
            dfResult:dfResult[index],
            persno:persno[index]
          }
        ))
        }))

NOTE: I updated the stackblitz to take account this

Upvotes: 1

Catleen
Catleen

Reputation: 81

A little snip from my table with how it looks like.

enter image description here

instead of

enter image description here

Upvotes: 0

Related Questions