Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically loading columns and data in to table in Angular 2

Tags:

angular

I have an HTML page in which I want to create a table. The columns in this table are dynamic which means that they are fetched from the server into a variable in the component using any[] type. The data in this table is also dynamic which means that at the time of programming I don't know which columns and data will come to get bound in the table.

I have tried below code but it doesn't seem to work or give any error. It just creates empty td in the tbody.

Expense.component.html

<div class="panel panel-default" style="margin-top:10px;">
<div class="panel-heading">
    Expenses
</div>
<div class="panel-body" style="position:relative">
    <div class="table-responsive">
        <table class="table">
            <thead>
                <tr>
                    <th *ngFor="#column of columns">
                        {{column}}
                    </th>
                </tr>
            </thead>
            <tbody>
                <tr *ngFor="#data of data">
                    <td *ngFor="#column of columns">
                        {{data.column}}
                    </td>
                </tr>
            </tbody>
        </table>
    </div>
</div>

In the above code, the columns are getting created successfully but the data and column mashup is not working. I am sure there must be a way in Angular 2 to achieve this.

Expense.component.ts

export class ExpenseComponent implements OnInit {
errorMessage: any[];
columns: string[];
data: any[];

constructor(private _commonService: CommonService, private _expenseService: ExpenseService) {

}

ngOnInit(): void {
    this._commonService.getColumnNames('condomanagement', 'expenses')
        .subscribe(data => this.promise(data), error => this.errorMessage = <any>error);
}

private promise(data: string[]) {
    this.columns = data;
    this._expenseService.getExpenses('condomanagement', this.columns)
        .subscribe(data => this.anotherPromise(data), error => this.errorMessage = <any>error);
}

private anotherPromise(data: any[]) {
    this.data = data;
    console.log(this.columns);
    console.log(this.data);
}

private handleError(error: Response) {
    console.error(error);
    return Observable.throw(error.json().error || 'Server error');
}
}

The data is getting logged in to console in the above code but not working in the HTML as per my trial. Any ideas, please?

Updated: Just used interpolation like this and it worked

{{mydata[column]}}

like image 778
user728630 Avatar asked Aug 07 '16 02:08

user728630


2 Answers

Instead of using

<tr *ngFor="#data of data">

You should use

<tr *ngFor="#data of data" *ngModel="data[column]">
like image 63
Lucas Tétreault Avatar answered Oct 14 '22 03:10

Lucas Tétreault


It can be used in Angular 2, 5, 6, 7. We should create a component, so that it can be used anytime, with any kind of data. I have tested it and is working.

File : dynamic-table.component.ts

import { Component, OnInit, Input, Output, OnChanges, SimpleChanges } from '@angular/core';

@Component({
  selector: 'dynamic-table',
  templateUrl: './dynamic-table.component.html',
  styleUrls: ['./dynamic-table.component.scss']
})
export class DynamicTableComponent implements OnInit, OnChanges {

  @Input() tableHeads: Array<String> = new Array<String>();
  @Input() tableDatas: Array<any> = new Array<any>();
  @Input() tableColName: Array<String> = new Array<String>();
  private tableColNameGenerated: Array<String> = new Array<String>();
  private isTableColNameSet: Boolean = false;

  constructor() { }

  ngOnInit() {

  }

  ngOnChanges(changes: SimpleChanges) {
    if (changes['tableHeads']) {
      if (this.tableHeads.length > 0) {
        // console.log('tableHeads');
      }
    }

    if (changes['tableDatas']) {
      if (!this.isTableColNameSet) {
        if (this.tableDatas.length > 0) {
          this.tableColNameGenerated = this.getKeys(this.tableDatas[0]);
          if (!this.isHeadAndColLengthSame(this.tableHeads, this.tableColNameGenerated)) {
            console.error('Table column row is not same as with property name in self generated');
         }
        }
      }
    }

    if (changes['tableColName']) {
      if (this.tableColName.length > 0) {
        this.tableColNameGenerated = this.tableColName;
        this.isTableColNameSet = true;
        if (!this.isHeadAndColLengthSame(this.tableHeads, this.tableColName)) {
          console.error('Table column row is not same as with property name provided');
        }
      }
    }
  }

  /**
  * This method will fetch all the property name and convert it into a list of String.
  * @param {Array<String>} head Pass in the list of String, which contains table header values
  * @param {Array<String>} col Pass in the list of String, which contains column property 
  * name, which was received from Input or generated using this.getKeys()
  */
  private isHeadAndColLengthSame(head: Array<String>, col: Array<String>): Boolean {
    return (head.length === col.length);
  }

  /**
  * This method will fetch all the property name and convert it into a list of String.
  * @param {any} value Pass Instance of Object eg. new User()
  */
  private getKeys(value: any): Array<String> {
    return Object.keys(value);
  }

}

File : dynamic-table.component.html

<table>
  <thead>
    <tr class="table-head">
      <th *ngFor="let tableHead of tableHeads">{{tableHead}}</th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let tableData of tableDatas">
      <td *ngFor="let colName of tableColNameGenerated"> {{tableData[colName]}}</td>
    </tr>
  </tbody>
</table>

To implement this component call in some view file

File : view-table.component.html

<div>
  <dynamic-table [tableHeads]="tableHead" 
                 [tableDatas]="userDetails" 
                 [tableColName]="tableColName">
  </dynamic-table>
</div>

File : view-table.component.ts

export class ViewTable implements OnInit{

  // fetch or create an Object of UserDetails type and pass it to dynamic-table
  private userDetails: Array<UserDetails>;
  // required to provide the table header, you can call an api or hard code the column name.
  private tableHead: Array<String>;  
  // optional, you can hard code the property name or just send the data of an object and dynamic-table component will figure out.
  private tableColName: Array<String>;  

  constructor(){
      this.tableHead = new Array<String>('Name', 'Age', 'Gender');
      // this.tableColName = new Array<String>('name', 'age', 'gender');
      this.userDetails = new Array<UserDetails>();
  }
   ngOnInit() {
      this.userDetails.push(new UserDetails('Apple', 18, 'Male'));
      this.userDetails.push(new UserDetails('Banana', 24, 'Female'));
      this.userDetails.push(new UserDetails('Mango', 34, 'Male'));
      this.userDetails.push(new UserDetails('Orange', 13, 'Female'));
      this.userDetails.push(new UserDetails('Guava', 56, 'Male'));
   }
}

export class UserDetails{
    constructor(public name: String, public age: Number, public gender: String) { }
}
like image 42
Pramod Kumar Sharma Avatar answered Oct 14 '22 05:10

Pramod Kumar Sharma