Rak
Rak

Reputation: 139

How to display from JSON object to DOM table where table column is based on JSON Keys

So I have this JSON object:

[
{
    "SysID": "4",
    "Defect Classification": "Wrong Image Color",
    "1": "3.0",
    "2": "",
    "3": "",
    "4": "",
    "5": "",
    "6": "",
    "7": "",
    "8": "",
    "9": "",
    "10": "",
    "11": "",
    "12": ""
},
{
    "SysID": "5",
    "Defect Classification": "Wrong Outer Liner Color",
    "1": "3.0",
    "2": "",
    "3": "",
    "4": "3.0",
    "5": "",
    "6": "",
    "7": "",
    "8": "",
    "9": "",
    "10": "",
    "11": "3.0",
    "12": ""
},
{
    "SysID": "6",
    "Defect Classification": "Critical Print Misalignment",
    "1": "",
    "2": "",
    "3": "",
    "4": "",
    "5": "",
    "6": "",
    "7": "",
    "8": "",
    "9": "",
    "10": "",
    "11": "",
    "12": ""
},
{
    "SysID": "7",
    "Defect Classification": ">15% Hole Placement Misalignment",
    "1": "",
    "2": "",
    "3": "3.0",
    "4": "",
    "5": "",
    "6": "",
    "7": "",
    "8": "",
    "9": "",
    "10": "",
    "11": "",
    "12": ""
},
{
    "SysID": "8",
    "Defect Classification": "Delamination",
    "1": "",
    "2": "",
    "3": "",
    "4": "",
    "5": "",
    "6": "",
    "7": "",
    "8": "",
    "9": "",
    "10": "",
    "11": "",
    "12": ""
},
{
    "SysID": "9",
    "Defect Classification": "Misaligned Lamination",
    "1": "",
    "2": "",
    "3": "",
    "4": "",
    "5": "",
    "6": "",
    "7": "",
    "8": "",
    "9": "",
    "10": "",
    "11": "",
    "12": ""
},
{
    "SysID": "10",
    "Defect Classification": "Poor Gluetab adhesion",
    "1": "",
    "2": "",
    "3": "",
    "4": "",
    "5": "",
    "6": "",
    "7": "",
    "8": "",
    "9": "",
    "10": "",
    "11": "",
    "12": ""
},
{
    "SysID": "11",
    "Defect Classification": "Skewing",
    "1": "",
    "2": "",
    "3": "",
    "4": "",
    "5": "",
    "6": "",
    "7": "",
    "8": "",
    "9": "",
    "10": "",
    "11": "",
    "12": ""
},
{
    "SysID": "12",
    "Defect Classification": "Panel Overlapping",
    "1": "",
    "2": "",
    "3": "",
    "4": "",
    "5": "",
    "6": "",
    "7": "",
    "8": "",
    "9": "",
    "10": "",
    "11": "",
    "12": ""
}
]

Now the object keys are not always the same. The numbers have a range from 1 to n. How to display this dynamically in a table??

This is my code so far:

component.ts

criticalData: any[] = [];
tableHeader: any[] = [];
this.apiservice.loadPrintDetail(id).subscribe({
    next: (data: any) => {
        this.criticalData = data;
        this.tableHeader = Object.keys(data[0]);
    }
})

component.html

<table class="table table-bordered table-hover " id="criticalTable">
                <thead>
                    <tr>
                        <th *ngFor="let item of tableHeader">{{item}}</th>
                    </tr>
                </thead>
                <tbody>
                    <tr *ngFor="let item of criticalData; let idx = index">
                        <td *ngFor="let row of criticalData; let ix = index">{{item[ix]}}</td>
                    </tr>
                </tbody>
            </table>

The result is this: Result

As you can see the result is so wrong. How to correct this? The SysID should be in the first column, Defect Classification should be in the second and columns 1 to n should be next. How to achieve this?

Upvotes: 0

Views: 107

Answers (5)

moh
moh

Reputation: 531

HTML.

<table class="table table-bordered table-hover " id="criticalTable">
    <thead>
        <tr>
            <th *ngFor="let item of data[0] | keyvalue : keyDescOrder"> {{item.key}}</th>
        </tr>
    </thead>
    <tbody>
        <tr *ngFor="let item of data">
            <td *ngFor="let item2 of item|keyvalue : keyDescOrder">
                <div>
                    {{item2.value}}
                </div>
            </td>
        </tr>
    </tbody>
</table>


TS.
import { KeyValue } from '@angular/common';
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-example1',
  templateUrl: './example1.component.html',
  styleUrls: ['./example1.component.css']
})
export class Example1Component implements OnInit {
 
  // Order by descending property key
  constructor() { }

  data: any = [
    {
      "SysID": "4",
      "Defect Classification": "Wrong Image Color",
      "1": "3.0",
      "2": "",
      "3": "",
      "4": "",
      "5": "",
      "6": "",
      "7": "",
      "8": "",
      "9": "",
      "10": "",
      "11": "",
      "12": ""
    },
    {
      "SysID": "5",
      "Defect Classification": "Wrong Outer Liner Color",
      "1": "3.0",
      "2": "",
      "3": "",
      "4": "3.0",
      "5": "",
      "6": "",
      "7": "",
      "8": "",
      "9": "",
      "10": "",
      "11": "3.0",
      "12": ""
    },
    {
      "SysID": "6",
      "Defect Classification": "Critical Print Misalignment",
      "1": "",
      "2": "",
      "3": "",
      "4": "",
      "5": "",
      "6": "",
      "7": "",
      "8": "",
      "9": "",
      "10": "",
      "11": "",
      "12": ""
    },
    {
      "SysID": "7",
      "Defect Classification": ">15% Hole Placement Misalignment",
      "1": "",
      "2": "",
      "3": "3.0",
      "4": "",
      "5": "",
      "6": "",
      "7": "",
      "8": "",
      "9": "",
      "10": "",
      "11": "",
      "12": ""
    },
    {
      "SysID": "8",
      "Defect Classification": "Delamination",
      "1": "",
      "2": "",
      "3": "",
      "4": "",
      "5": "",
      "6": "",
      "7": "",
      "8": "",
      "9": "",
      "10": "",
      "11": "",
      "12": ""
    },
    {
      "SysID": "9",
      "Defect Classification": "Misaligned Lamination",
      "1": "",
      "2": "",
      "3": "",
      "4": "",
      "5": "",
      "6": "",
      "7": "",
      "8": "",
      "9": "",
      "10": "",
      "11": "",
      "12": ""
    },
    {
      "SysID": "10",
      "Defect Classification": "Poor Gluetab adhesion",
      "1": "",
      "2": "",
      "3": "",
      "4": "",
      "5": "",
      "6": "",
      "7": "",
      "8": "",
      "9": "",
      "10": "",
      "11": "",
      "12": ""
    },
    {
      "SysID": "11",
      "Defect Classification": "Skewing",
      "1": "",
      "2": "",
      "3": "",
      "4": "",
      "5": "",
      "6": "",
      "7": "",
      "8": "",
      "9": "",
      "10": "",
      "11": "",
      "12": ""
    },
    {
      "SysID": "12",
      "Defect Classification": "Panel Overlapping",
      "1": "",
      "2": "",
      "3": "",
      "4": "",
      "5": "",
      "6": "",
      "7": "",
      "8": "",
      "9": "",
      "10": "",
      "11": "",
      "12": ""
    }
  ]
  ngOnInit(): void {
    // this.data = this.data.sort();
    console.log(this.data);
    
  }

  keyDescOrder = (a: KeyValue<number,string>, b: KeyValue<number,string>): number => {
    if(isNaN(a.key)){
      return a.key > b.key ? -1 : (b.key > a.key ? 1 : 0);
    }else{
       return 1;
     }
  }

}

Upvotes: 1

Rehan Khan
Rehan Khan

Reputation: 154

As Christian Joseph Dalisay stated, Following is the simple way to do:

//Get First Object from array
let rawCols = data[0];
//Add default two columns
tableHeader: any = ["SysID", "Defect Classification"];
//Add the rest dynamic columns
for(var key in rawCols){
       if(tableHeader.indexOf(key) === -1) 
         tableHeader.push(key);
    }

//Front end
<table class="table table-bordered table-hover " id="criticalTable">
<thead>
    <tr>
        <th *ngFor="let item of tableHeader">{{item}}</th>
    </tr>
</thead>
<tbody>
    <tr *ngFor="let item of items">
        <td *ngFor="let cols of columns">
          <div>
            {{item[cols]}}
          </div>
        </td>
    </tr>
</tbody>
</table>

Upvotes: 1

You can add an array indicating SysID and Defect Classification as the first columns followed by the indexes.

For the header, you populate the columns. For the rows and items, you nest for loop the items, but displaying only the item based on the column header

<table class="table table-bordered table-hover " id="criticalTable">
<thead>
    <tr>
        <th *ngFor="let item of columns">{{item.name}}</th>
    </tr>
</thead>
<tbody>
    <tr *ngFor="let item of items">
        <td *ngFor="let item2 of columns">
          <div>
            {{item[item2.name]}}
          </div>
        </td>
    </tr>
</tbody>
</table>

On the typescript file, you assign the column index for each header. And assign the remaining indexes as the succeeding indexes.

  columns: any[] = [
    {
      columnNo: 0,
      name: 'SysID',
    },
    {
      columnNo: 1,
      name: 'Defect Classification',
    },
  ];

  ngOnInit() {
    let noOfColumns = 12;
    let numberedColumns = [...Array(noOfColumns).keys()].map((item) => {
      return {
        columnNo: this.columns.length + item,
        name: item + 1,
      };
    });
    this.columns = [...this.columns, ...numberedColumns];
  }

You can check this link I created https://angular-mxcyxg.stackblitz.io

Upvotes: 0

Sergej
Sergej

Reputation: 2176

Object.keys() does not guarantee that the columns order will be the same, as in the original object. See here: Does JavaScript guarantee object property order?

I would suggest you to use the | keyvalue pipe in your html template to achieve the thing you want. I believe the pipe will not change the order of the columns, for sure, you can use compare function to keep the original order. See here: angular keyvalue pipe sort properties / iterate in order

Upvotes: 1

FunkMonkey33
FunkMonkey33

Reputation: 2258

I can pretty much guarantee that that is the order your data is coming in. You should set a breakpoint or console log the data to sure.

If you have no control over how the data arrives (what you present above isn't super convenient), then you'll have to transform the data in code, probably in your loadPrintDetail() subscription.

BTW you should unsubscribe from your observables or use a .pipe(take(1)) to avoid a memory leak.

Upvotes: 1

Related Questions