I have a template of 3 tables having same JSON as parent like this.
<tbody>
<ng-container *ngFor="let row of reportingData.RecommendationData; let i=index">
<tr *ngIf="row.swRecommendations">
<td>{{i+1}}</td>
<td> {{row.swRecommendations.deviceID}}</td>
</tr>
</ng-container>
</tbody>
Another table body
<tbody>
<ng-container *ngFor="let row of reportingData.RecommendationData; let j=index">
<tr *ngIf="row.licenseRecommendations">
<td>{{j+1}}</td>
<td> {{row.licenseRecommendations.deviceID}}</td>
</tr>
</ng-container>
</tbody>
All these tables are in the same template. I'm assigning index values to different variables(i & j) but increment is happening i.e. if first table is having 5 rows, second table is starting with 6 not 1. How to fix this?
I tested you'r code and indexes are starting from 0 .
Please review my code.
Component.html
<h1>First Table</h1>
<table>
<tr>
<th>Id</th>
<th>Name</th>
</tr>
<ng-container>
<tr *ngFor="let a of array;let i = index">
<th>{{i + 1}}</th>
<th>{{a.name}}</th>
</tr>
</ng-container>
</table>
<h1>Second Table</h1>
<table>
<tr>
<th>Id</th>
<th>Name</th>
</tr>
<ng-container>
<tr *ngFor="let a of array;let j = index">
<th>{{j + 1}}</th>
<th>{{a.name}}</th>
</tr>
</ng-container>
</table>
Component.ts
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styles: ['./app.component.scss']
})
export class AppComponent {
constructor() {}
public array = [
{ id: 1, name: 'aaa'},
{ id: 2, name: 'bbb'},
{ id: 3, name: 'ccc'},
{ id: 4, name: 'ddd'},
{ id: 5, name: 'eee'},
{ id: 6, name: 'fff'},
]
}
H recently faced the same issue, since we are indexing it and increment it will be like that, the solution of this problem is like this Filter your data in the ts
this.a= this.reportingData.filter(x => x.swRecommendations);
this.b= this.reportingData.filter(x => x.licenseRecommendations);
<tr *ngFor="let row of a"> <tr *ngFor="let row of b">,
and then remove the if condition and iterate the data in HTML like this let row of reportingData , editing needed based on your consition in ts
Related
Hi I am trying to create a table with rowspan based on array lengths. It works for me with 2 nested arrays but I have no idea ho to do it with more nest levels.
For data:
data = [
{ id: 1, name: 'one', groups:
[{'name': 'gr1', campaigns :
[{'name' : 'camp1'}, {'name' : 'camp2'}, {'name' : 'camp3'}]},
{'name': 'gr2', campaigns :
[{'name' : 'camp4'}, {'name' : 'camp5'}, {'name' : 'camp6'}]}
] },
{ id: 2, name: 'two', groups: [{'name': 'gr3', campaigns : [{'name' : 'camp7'}]}] },
{ id: 3, name: 'three', groups: [{'name': 'gr4', campaigns : [{'name' : 'camp8'}]}] },
{ id: 4, name: 'four', groups: [{'name': 'gr5', campaigns : [{'name' : 'camp9'}]}] }
];
I tried:
<table class="table table-bordered table-hover hidder" id="report">
<!-- Header -->
<tr>
<th >Project</th>
<th>Group</th>
<th>Camps</th>
</tr>
<ng-container *ngFor="let project of data; let i = index;">
<tr>
<td [attr.rowspanspan]="project.groups.length"> {{project.name}} </td>
<ng-container *ngFor="let group of project.groups; let j = index">
<tr>
<td>
{{ group.name }}
</td>
<!-- <ng-container *ngFor="let campaign of group.campaigns; let k = index">
<tr>
<td >
{{ campaign.name }}
</td>
</ng-container> -->
</tr>
</ng-container>
</ng-container>
</table>
My goal is to get table like this in picture:
image example
For exampleI have code here:
working code example
The nature of the table structure in HTML makes it quite difficult to split it programmatically. You could try the following, but then you'll have to style the table yourself:
<table class="table table-bordered table-hover hidder" id="report">
<!-- Header -->
<tr>
<th>Project</th>
<th>Group</th>
<th>Camps</th>
</tr>
<ng-container *ngFor="let project of data; let i = index">
<tr>
<td>{{ project.name }}</td>
<td>
<table>
<ng-container *ngFor="let group of project.groups; let j = index">
<tr click="test(group);">
<td>{{ group.name }}</td>
</tr>
</ng-container>
</table>
</td>
<td>
<table>
<ng-container *ngFor="let group of project.groups; let j = index">
<ng-container *ngFor="let campaign of group.campaigns; let k = index">
<tr click="test(group);">
<td>{{ campaign.name }}</td>
</tr>
</ng-container>
</ng-container>
</table>
</td>
</tr>
</ng-container>
</table>
Check out the StackBlitz example.
Should be [attr.rowspan], but I think you should remove it, see your forked stackblitz.
NOTE: I added in the .css to override the bootstrap padding of a table
.table > :not(caption) > * > *
{
padding:0 .5rem;
}
NOTE2: we use [attr.rowspan] if we have a "flat" array, like this another SO. not when we have an array with "groups"
i'm currently working on displaying large amounts of data in tables for a scientific project. i have timeStamps of experiments, which look like this:
interface TimeData {
time: string;
data: {SD: string, SEM: string, N: string, MEAN: string};
}
i have a total of 11 timeStamps in the table, all described by their 'time', e.g. 15, 30, 60, 90...
if an experiment is missing a timestamp completely or has no data for the timestamp 30 at e.g. MEAN, i want to print -- in the td.
i can't seem to wrap my head around how to display all mean data in a row and just replacing missing one's with a '--'...
i've tried wrapping in ng-container, several ngFor loops, yet it always comes out wrong, i.e. i get too many -- or none at all and my data is being displayed in the wrong td.
here's the html
<table class="table table-bordered">
<thead>
<tr>
<th scope="col">Code</th>
<th scope="col">Function</th>
<th scope="col" class="text-center"
*ngFor="let timeStamp of maxTimeStamps; let i=index">t{{i + 1}}
{{timeStamp.time}}</th>
<th>ΔAUC</th>
</tr>
</thead>
<tbody *ngFor="let experiment of report.experiments; let i = index">
<tr>
<td rowspan="4" [ngStyle]="{color: experimentColors[i]}">{{experiment.code.toUpperCase()}}</td>
<td>mean</td>
------missing td goes here-------
----------------------------------
</tr>
</tbody>
</table>
Welcome to StackOverflow, If I get it right you want to show your data in rows but you add ngFor in the wrong place for that (it shouldn't add to tbody tag)! please check the corrected code below.
You can change or make a condition by ngIf if you don't want to add that td.
Good Luck.
<table class="table table-bordered">
<thead>
<tr>
<th scope="col">Code</th>
<th scope="col">Function</th>
<th scope="col" class="text-center"
*ngFor="let timeStamp of maxTimeStamps; let i=index">t{{i + 1}}
{{timeStamp.time}}</th>
<th>ΔAUC</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let experiment of report.experiments; let i = index">
<td rowspan="4" [ngStyle]="{color: experiment.Colors[i]}" [ngIf]="experiment.CheckableItem == true">{{experiment.code.toUpperCase()}}</td>
<td>mean</td>
</tr>
</tbody>
</table>
If some of the properties will not be available in the interface then they should be defined optional. Notice the question mark.
interface TimeData {
time?: string;
data?: {SD?: string, SEM?: string, N?: string, MEAN?: string};
}
In the template, you could use *ngIf directive with an else clause to show '--' if some properties are not available. Try the following
Controller
import { Component, VERSION, ElementRef } from '#angular/core';
import { Router } from '#angular/router';
interface TimeData {
time?: string;
data?: {SD?: string, SEM?: string, N?: string, MEAN?: string};
}
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
timeData: TimeData[] = [
{time: '15', data: {SD: '10', SEM: '20', N: '30', MEAN: '75'}},
{data: {SD: '10', SEM: '20', N: '30', MEAN: '75'}},
{time: '15', data: {SD: '10', SEM: '20', N: '30'}},
{data: {SEM: '20', N: '30', MEAN: '75'}},
];
constructor(private _router: Router, private _el: ElementRef) { }
}
Template
<table>
<tr>
<th>Time</th>
<th>SD</th>
<th>SEM</th>
<th>N</th>
<th>MEAN</th>
</tr>
<tr *ngFor="let data of timeData">
<ng-container *ngIf="data.time; else emptyData">
<td>{{ data.time }}</td>
</ng-container>
<ng-container *ngIf="data.data.SD; else emptyData">
<td>{{ data.data.SD }}</td>
</ng-container>
<ng-container *ngIf="data.data.SEM; else emptyData">
<td>{{ data.data.SEM }}</td>
</ng-container>
<ng-container *ngIf="data.data.N; else emptyData">
<td>{{ data.data.N }}</td>
</ng-container>
<ng-container *ngIf="data.data.MEAN; else emptyData">
<td>{{ data.data.MEAN }}</td>
</ng-container>
<ng-template #emptyData>
<td>--</td>
</ng-template>
</tr>
</table>
Working example: Stackblitz
I have a table containing some rows.The table data comes from loop.Here I need to add new columns based on selection of checkbox.Suppose I checked product,a new table heading will create as product along with blank rows just below the table heading,again If I uncheck created columns will be removed.New columns should create before add button.Here is the code below
https://stackblitz.com/edit/angular-table-focus
app.component.html
<div class="container">
<h2>Basic Table</h2>
<p>The .table class adds basic styling (light padding and only horizontal dividers) to a table:</p>
<div><button (click)="enable()">Edit</button> <input type="checkbox" value="product">Product <input type="checkbox" value="market">market</div>
<table class="table border">
<thead>
<tr>
<th>Items</th>
<th>Business</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of groups;let i = index" (click)="setClickedRow(i)" [class.active]="i == selectedRow">
<td> <input #focus [disabled]='toggleButton' type="text" value="{{row.name}}"> </td>
<td> <input [disabled]='toggleButton' type="text" value="{{row.items}}"> </td>
<td> <button (click)="addRow(i)">Add</button></td>
</tr>
</tbody>
</table>
</div>
app.component.ts
import { Component,ViewChild, ElementRef } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
selectedRow : Number;
#ViewChild('focus') input: ElementRef;
public toggleButton: boolean = true;
ngOnInit() {
}
groups=[
{
"name": "pencils",
"items": "red pencil"
},
{
"name": "rubbers",
"items": "big rubber"
},
{
"name": "rubbers1",
"items": "big rubber1"
},
];
addRow(index): void {
var currentElement = this.groups[index];
this.groups.splice(index, 0, currentElement);
}
enable(){
this.toggleButton = false
setTimeout(()=>{ // this will make the execution after the above boolean has changed
this.input.nativeElement.focus();
this.selectedRow = 0;
},0);
}
setClickedRow(index) {
this.selectedRow = index;
}
}
Check out my StackBlitz for live demo: https://stackblitz.com/edit/angular-table-focus-77xrnn
To add or remove columns dynamically, we have to first fetch the values from the component instead of hard coding them in the template.
columns = ["Items", "Business"];
Component:
And whenever a column checkbox is toggled, we listen to the change event and add or delete the column accordingly.
onColumnStatusChange(column, isChecked) {
if (isChecked) {
// Add column to the table.
this.columns.push(column);
this.groups.forEach(value => {
value[column] = "";
});
} else {
// Delete column from the table.
this.columns.splice(this.columns.indexOf(column), 1);
this.groups.forEach(value => {
delete value[column];
});
}
}
Template:
In the template, columns and also table row items must be fetched dynamically.
<input type="checkbox" value="product" (change)="onColumnStatusChange($event.target.value, $event.target.checked)">Product
<input type="checkbox" value="market" (change)="onColumnStatusChange($event.target.value, $event.target.checked)">market
...
...
<tr>
<ng-container *ngFor="let column of columns; let i = index">
<th>{{ column }}</th>
</ng-container>
</tr>
...
...
<tr *ngFor="let row of groups;let i = index" (click)="setClickedRow(i)" [class.active]="i == selectedRow">
<ng-container *ngFor="let item of objectKeys(row); let j = index">
<td><input #focus [disabled]='toggleButton' type="text" value="{{row[item]}}"></td>
</ng-container>
<td><button (click)="addRow(i)">Add</button></td>
</tr>
I have this code which parses JSON and list data into the table using ngFor.
The result will be like.
Yes, listing data into the table is working fine.
But how can I implement it to output single data of specific ID?
For example - I want the name of id=3 the output should be "Edward".
I can't figure out how to do this. Can someone help me or guide me?
Thanks.
app-components.ts
import { Component } from '#angular/core';
import {Http, Response} from '#angular/http';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
private data;
constructor(private http:Http){
}
ngOnInit(){
this.getData();
}
getData(){
this.http.get('/localhost/data.json')
.subscribe(res => this.data = res.json());
}
}
app-component.html
<table>
<tr>
<th>#</th>
<th>Name</th>
<th>Amount</th>
</tr>
<tr *ngFor="let info of data">
<td>{{info.id}}</td>
<td>{{info.name}}</td>
<td>{{info.amount}}</td>
</tr>
</table>
data.json
[
{
"id":"1",
"name":"John",
"amount":"112"
},
{
"id":"2",
"name":"Maria",
"amount":"44"
},
{
"id":"3",
"name":"Edward",
"amount":"40"
}
]
Please tell me that am I providing sufficient information or not.
You can use <ng-container> for the same, this have to use because you cannot apply two directives on a single element like we are using *ngIf and *ngFor.
<table>
<tr>
<th>#</th>
<th>Name</th>
<th>Amount</th>
</tr>
<ng-container *ngFor="let info of data">
<tr *ngIf='info.id == 3'>
<td>{{info.id}}</td>
<td>{{info.name}}</td>
<td>{{info.amount}}</td>
</tr>
</ng-container>
</table >
working example
Instead of adding an *ngIf directive , why don't you create a component separately to output only one table row ?
For example :
table.component.html will have :
<table>
<tr>
<th>#</th>
<th>Name</th>
<th>Amount</th>
</tr>
<tr>
<td>{{info.id}}</td>
<td>{{info.name}}</td>
<td>{{info.amount}}</td>
</tr>
</table>
and table.component.ts will have :
#Input() info : any;
Now you can loop over the <app-table [info]='info'></app-table> inside app-component.html also , you can just pass <app-table [info]='info[3]'></app-table> .
As it looks in the photo, that I want is, when I click show more, I want to expand this row, and show me all sensors in there
I'm using materialize, and my code html is:
<table class="bordered table-bordered" [mfData]="homeboxsp | dataFilter : filterQuery" #mf="mfDataTable" [mfRowsOnPage]="rowsOnPage"
[(mfSortBy)]="sortBy" [(mfSortOrder)]="sortOrder">
<thead style="color:black; background:rgb(207, 235, 245);border:1px solid rgb(190, 190, 190);">
<tr>
<th>
<mfDefaultSorter by="client">Client</mfDefaultSorter>
</th>
<th>
<mfDefaultSorter by="description">Homebox</mfDefaultSorter>
</th>
<th>
<mfDefaultSorter by="sensors">Sensors</mfDefaultSorter>
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of homeboxsp">
<td>{{item.client}}</td>
<td>{{item.description}}</td>
<td>
<ul>
<ul *ngFor="let sensor of item.sensors">
<li>{{sensor.sensor_serial}}</li>
</ul>
</ul>
</td>
</tr>
</tbody>
</table>
you can do this in simple way using ngIf
<ul *ngFor="let sensor of item.sensors;let i = index;">
<li *ngIf="i < value">{{sensor.sensor_serial}}</li>
<button (click)="showmore()">showmore</button>
</ul>
ts
value:number = 2;
showmore(){
let value = this.value;
this.value= value+1;
}
you can modify logic as you want
The SlicePipe is perfect for this kind of situations. It does pretty much the same as the accepted answer with fewer *ngIf needed.
#Component({
selector: 'my-app',
template: `
<div>
<div *ngFor="let val of values | slice:0:visible">{{val}}</div>
<div *ngIf="visible==3" (click)="visible=values.length">show more</div>
<div *ngIf="visible==values.length" (click)="visible=3">show less</div>
</div>
`,
})
export class App {
visible= 3;
values = [1, 2,3,4, 5,6,7,8,9]
constructor() {
}
}
You can see a working example here.
The same principle applies if you want to have multiple lists that expand. You just need to control the visibility for each list separately.
Here's an example of that.
#Component({
selector: 'my-app',
template: `
<div>
<div *ngFor="let group of groups">
<div>{{group.name}}</div>
<div *ngFor="let val of group.values | slice:0:(group.expanded? group.values.length :3)">{{val}}</div>
<div *ngIf="!group.expanded" (click)="group.expanded=true">show more</div>
<div *ngIf="group.expanded" (click)="group.expanded=false">show less</div>
</div>
</div>
`,
})
export class App {
groups = [{name: 'a', values: [1, 2,3,4, 5,6,7,8,9]},{name: 'b', values: [1, 2,3,4, 5,6,7,8,9]}]
constructor() {
}
}
And the working plnkr here.
You can build a simple component that can show just a certain number of sensors, or adjust it to show just certain height:
<td><values-display-cmp [values]="row"></values-display-cmp></td>
Then the component:
#Component({
selector: 'values-display-cmp',
template: `
<ul>
<li *ngFor="let val of shownValues">{{val}}</li>
</ul>
More
`
})
export class ValuesDisplayComponent {
#Input() set values(values) {
this._values = values;
this.shownValues = values.slice(0, 2);
};
open = false;
shownValues = [];
_values = [];
more() {
this.open = true;
this.shownValues = this._values;
}
}
Here's a full example.
Separate your array in 2 parts, and after clicking show more concat them. Seems like not a big deal.