Angular dynamic table using ngFor - html

I would like to know if it is possible to create a dynamic HTML table from JSON data. The amount of columns and headers should change according to the keys in the JSON. For example this JSON should create this table:
{
color: "green", code: "#JSH810"
}
,
{
color: "red", code: "#HF59LD"
}
...
And this JSON should create this table:
{
id: "1", type: "bus", make: "VW", color: "white"
}
,
{
id: "2", type: "taxi", make: "BMW", color: "blue"
}
...
This has to be 100% dynamic though, because I want to display hundreds of different JSON objects, so nothing should be hard coded in the HTML page.

If you want to get the key of your object as the head of your table, you should create a custom pipe.
import { PipeTransform, Pipe } from '#angular/core';
#Pipe({name: 'keys'})
export class KeysPipe implements PipeTransform {
transform(value, args:string[]) : any {
let keys = [];
for (let key in value) {
keys.push(key);
}
return keys;
}
}
Update: Or simply return keys using Object.keys().
(demo)
#Pipe({name: 'keys'})
export class KeysPipe implements PipeTransform {
transform(value, args:string[]) : any {
return Object.keys(value);
}
}
Now into your html template:
<table>
<thead>
<tr>
<th *ngFor="let head of items[0] | keys">{{head}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of items">
<td *ngFor="let list of item | keys">{{item[list]}}</td>
</tr>
</tbody>
</table>
Update: Here is the demo.

This can help:
export class AppComponent {
//Array of any value
jsonData:any = [
{id: "1", type: "bus", make: "VW", color: "white"},
{id: "2", type: "taxi", make: "BMW", color: "blue"}
];
_object = Object;
}
<div>
<table border="1">
<thead>
<tr>
<th *ngFor="let header of _object.keys(jsonData[0]); let i = index">{{header}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of jsonData; let i = index">
<th *ngFor="let objKey of _object.keys(row); let j = index">{{ row[objKey] }} </th>
</tr>
</tbody>
</table>
</div>

Related

How can I make Angular model work in html page?

I connect with my service without any problem, then I import it into a model, but when I try to print the data with a for loop in the html page, it comes up blank. Where do you think I am doing wrong?
this is component file
import { Component, OnInit, ViewChild } from '#angular/core';
import { ResultsModel } from 'src/app/core/models/results.model';
#Component({
selector: 'app-results',
templateUrl: './results.component.html',
styleUrls: ['./results.component.scss']
})
export class ResultsComponent implements OnInit {
clientResults : ResultsModel[] = [];
constructor(private httpRequestService : HttpRequestService) {}
ngOnInit(): void {
this.getAllResults();
}
getAllResults(){
let apiEndpoint = "results"
this.httpRequestService.getApi(apiEndpoint, false).subscribe(resultRequest => {
this.clientResults = resultRequest
return this.clientResults
})
}
this is model file
export interface ResultsModel {
"result": string,
"createddate": string,
"clientid": string,
"id": number,
"clientusername": string,
}
this is html file
<table class="table align-middle table-nowrap" id="invoiceTable">
<thead class="text-muted">
<tr>
<th class="sort text-uppercase" data-sort="invoice_id">User Id</th>
<th class="sort text-uppercase" data-sort="customer_name">Username</th>
<th class="sort text-uppercase" data-sort="email">Type</th>
<th class="sort text-uppercase" data-sort="date">Type</th>
<th class="sort text-uppercase" data-sort="invoice_amount">Type</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let results in clientResults">
<td>{{results.id}}</td>
<td class="customer_name">{{results.username}}</td>
<td>{{results.clientid}}</td>
<td>{{results.result}}</td>
<td>{{results.createddate}}</td>
</tr>
</tbody>
</table>
Where do you think I went wrong? Where could I be doing wrong?

Put json data into table

I actually want to create a table with the data who the api return to me.
The problem is that i can't print the data.
The IdLangage have his column in the table and i want to put the data of the traduction into the correct cell.
The JSON data format :
traductionData ={
"Data":
[
{
"Code": "BJR",
"TraductionsFormat":
[{
"Code": "BJR",
"Description": null,
"Id": 0,
"IdLangage": "FR",
"Traduction": "Bonjour"
},
{
"Code": "BJR",
"Description": null,
"Id": 0,
"IdLangage": "EN",
"Traduction": "Hello"
}]
},
] };
Here is my table where i want to print the data into :
<table>
<thead>
<tr>
<th width="25%">Code</th>
<th width="15%">FR</th>
<th width="15%">EN</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let traduction of traductionData">
<td>{{ traduction.TraductionsFormat.Code }}</td>
<td>{{ traduction.TraductionsFormat.Traduction}}</td>
</tr>
</tbody>
</table>
Here is my angular service :
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http'
import { map } from 'rxjs/operators';
import { environment } from 'src/environments/environment';
#Injectable({
providedIn: 'root'
})
export class ApiService {
localUrlAPI: string = environment.urlAPI;
constructor(private http : HttpClient) { }
getAllTraductions(){
return this.http.get<any>(this.localUrlAPI+"GetAllTraductionsGroupByCode")
.pipe(map((res:any)=>{
return res;
console.log(res);
}))
}
}
And here is my angular Component with my http request :
import { Component, OnInit } from '#angular/core';
import { ApiService } from 'src/app/services/api.service';
#Component({
selector: 'app-grid-edit-traductions',
templateUrl: './grid-edit-traductions.component.html',
styleUrls: ['./grid-edit-traductions.component.scss']
})
export class GridEditTraductionsComponent implements OnInit {
traductionData !: any[];
constructor(private api: ApiService) { }
ngOnInit(): void {
this.getLesTraductions();
}
getLesTraductions(){
this.api.getAllTraductions()
.subscribe(res=>{
this.traductionData = res.Data;
console.log(this.traductionData)
})
}
}
<table>
<thead>
<tr>
<th *ngFor="let column of tableHeaders">
{{column}}
</th>
</tr>
</thead>
<tbody>
<tr ng *ngFor="let row of tableRows">
<td *ngFor="let column of tableHeaders">
{{row[column]}}
<ng-container *ngFor="let trad of row.TraductionsFormat, let j = index">
<span *ngIf="row.TraductionsFormat[j].IdLangage === column">
{{row.TraductionsFormat[j].Traduction}}
</span>
</ng-container>
</td>
</tr>
</tbody>
</table>
Here's the ts:
tableRows: Array<any> = [];
tableHeaders: Array<any> = [];
ngOnInit(): void {
//---- TABLE HEADERS -----
this.tableHeaders.push("Code")
this.traductionData.Data.forEach(el => {
el.TraductionsFormat.map(c => c.IdLangage).forEach(lang => {
this.tableHeaders.push(lang);
})
});
this.tableHeaders = [...new Set(this.tableHeaders)];
//---- TABLE ROWS -----
this.traductionData.Data.forEach(el => {
this.tableRows.push(el)
});
}
Stackblitz example
The JSON data you've provided is wrong, there are missing commas and brackets. Although, I'm pretty sure that the reason the data isn't shown in table is that the "TraductionsFormat" is an array. If you want to get an item from array you have to provide an index.
<tr *ngFor="let traduction of traductionData">
<td>{{ traduction.TraductionsFormat[0].Code }}</td>
<td>{{ traduction.TraductionsFormat[0].Traduction}}</td>
</tr>
Above is just simple solution. You might want to use dynamic indexes.

Angular: data is not showing up in UI

I am trying to access values in a list object, but data not showing up in UI, not sure why?
i can see the data in the console log. Below are the angular model interface i am using and
angular class,HTML code and Mock data, what am i doing wrong ?
Interfaces
export interface MycodeType {
SpecialCode?: Mycodes[];
}
export interface Mycodes {
SpeicalCode: number;
SpecialCodeDescription: string;
SpecialProvidedStatus: string;
}
My Angular Component file:
export class SpecialCodesComponent implements OnInit {
#Input('sFC') specialFeatureCodes: MycodeType;
constructor() {}
specialCodesColumns: any[];
ngOnInit(): void {
this.specialFeatureCodesColumns = [
{ field: 'specialCode', header: 'Special Code', width:'125%' },
{ field: 'specialDescription', header: 'Special Code Description', width:'75%' },
{ field: 'providedDerived', header: 'Provided/Derived', width:'125%' },
];
}
}
HTML:
<div *ngIf="specialCodes?.SpecialCode.length>0" class="row"
style="padding-left: 0px;padding-right: 25px; text-align:center">
<fm-table [columns]="specialCodesColumns" [value]="specialCodes.SpecialCode">
<ng-template fmTemplate="header" let-columns>
<tr>
<th *ngFor="let col of columns" style="text-align: center; font-size: 15px;">{{col.header}}</th>
</tr>
</ng-template>
<ng-template fmTemplate="body" let-rowData let-columns="columns">
In the console log i see the data
SpecialCode: Array [ {…}, {…} ]
*But data is not showing up in UI
Mock data is that i am using to populate on UI screen:
"SpecialCodes":{
"SpecialCode":[
{
"SpecialCode": "118",
"SpecialCodeDescription": "some Second data",
"SpecialCodeProvidedStatus": "Provided"
},
{
"SpecialCode": "127",
"SpecialCodeDescription": "some data test",
"SpecialCodeProvidedStatus": "Derived"
}

How to filter JSON object depends upon key name in angular 2?

In my angular 2 application Headers are dynamically setting up. I arranged my JSON data as shown in attached image. I want to filter entire data using the Table Heading as a search term (Ex: Name or Position or Extn).
I am separating my result data to keys and values like:
this.keys= Object.keys(this.ResultData_search[0])` // my table heading false here
in my template :
<table>
<tr>
<th *ngFor=" let key of keys,let i=index"></th>
</tr>
<tr *ngFor=" let res of ResultData_search ,let i = index" style="height: 35px;">
<td>{{i+1}}</td>
<td *ngFor=" let key of keys ">
{{res[key]}}
</td>
</tr>
</table
Why I am doing this is because my JSON data is not static. I have to set table headings and data dynamically.
You have to use a Pipe with an Array parameter containing the filters for each header.
In the Pipe you can dynamically filter your data for each header.
Let's assume you have the following component with a similar data structure as yours. It has a filters Array of type IFilter :
import { Component, OnInit, ChangeDetectionStrategy } from '#angular/core';
#Component({
selector: 'app-filter',
templateUrl: './filter.component.html',
styleUrls: ['./filter.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class FilterComponent implements OnInit {
data: Array<Map<string, string>>;
headers: Array<string>;
filters: Array<Ifilter>;
constructor() {
}
ngOnInit() {
this.data = new Array(
new Map([["header1", "value11"], ["header2", "value12"], ["header3", "value13"], ["header4", "value14"], ["header5", "value15"]]),
new Map([["header1", "value21"], ["header2", "value22"], ["header3", "value23"], ["header4", "value24"], ["header5", "value25"]]),
new Map([["header1", "value31"], ["header2", "value32"], ["header3", "value33"], ["header4", "value34"], ["header5", "value35"]])
);
this.headers = Array.from((this.data[0]).keys());
this.filters = new Array({header:"header1",filter:""},{header:"header2",filter:""},{header:"header3",filter:""},{header:"header4",filter:""},{header:"header5",filter:""})
}
}
export interface Ifilter{
header : string;
filter : string;
}
Then define the following Pipe and filter for each header.
import { Pipe, PipeTransform } from '#angular/core';
import { Ifilter } from "app/filter/filter.component";
#Pipe({
name: 'pipeFilter',
pure: false
})
export class FilterPipe implements PipeTransform {
transform(data: Array<Map<string, string>>, filters: Array<Ifilter>): Array<Map<string, string>> {
let filteredData = Array<Map<string, string>>();
for (let row of data) {
let exclude: boolean = false;
for (let filter of filters) {
if (filter.filter != '' && row.get(filter.header).indexOf(filter.filter) == -1) {
exclude = true;
break;
}
}
if (!exclude)
filteredData.push(row);
}
return filteredData;
}
}
Finally, display the table with this template.
Get the filters with a two-way binding [(NgModel)] and pass them as parameter to the Pipe on the *ngFor : *ngFor="let rows of data | pipeFilter:filters". :
<table *ngIf="data && data.length">
<th *ngFor="let header of headers">
{{header}}
</th>
<tr>
<td *ngFor="let filter of filters;let i=index">
<input type='text' [(ngModel)]='(filters[i]).filter' />
</td>
</tr>
<tr *ngFor="let rows of data | pipeFilter:filters">
<td *ngFor="let header of headers ">
{{rows.get(header)}}
</td>
</tr>
</table>
Both Template and Pipe are completely dynamic and can handle any header.
Regard,
Philippe

Angular2: assign component field within #ngFor

I have such component:
#Component({
selector: "expenses-component",
templateUrl: "expenses.html"
})
export default class ExpensesComponent {
private expenses: [] = [{name: "Foo", amount: 100}, {name: "Bar", amount: 200}];
private totalAmount: number = 0;
}
and also such template:
<h2 class="sub-header">Total: {{total | currency:"USD":true}}</h2>
<div class="table-responsive">
<table class="table table-hover table-condensed">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Amount</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let expense of expenses | orderByDate; let id = index">
<td>{{id + 1}}</td>
<td>{{expense.name}}</td>
<td>{{expense.amount | currency:"USD":true}}</td>
</tr>
</tbody>
</table>
</div>
I need to count totalAmount, but i can't figure out how to do it without providing another one for loop in typescript code, so is there possibility to initialize total += expense.amount within the above #ngFor loop?
As far as I know, it is not possible. You can achieve it by creating custom pipe which will iterate through the array and return totalAmount. As the parameter you can pass the name of the key which you want to use as value.
For the example:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'totalAmount'
})
export class totalAmount implements PipeTransform {
transform(value:any, key:string):any {
if(value){
let total = 0;
for(let i in value){
total = total + value[i][key];
}
return total;
}
return value;
}}
And then you can call it from html:
<div>{{expenses | totalAmount:"amount"}}</div>
You could make totalAmount a get() method and return it there?
private get totalAmount() {
let total = 0;
for (let item in this.expenses) {
total += item.amount;
}
return total;
}