how to retrieve the value from nested object arrays angular - html

I have an array of objects with nested arrays as shown below. How can I print the nested array values in a table using ngFor.
The array looks as follows:
I am using a table to print this values so I can export the table to Excel sheet.
The table looks as follows:
<table class="table table-bordered">
<thead class="thead-dark">
<tr>
<th>Hours</th>
<th>Dates</th>
<th>Project Codes</th>
</tr>
</thead>
<tbody class="tbody" *ngFor="let value of array; let i = index">
<tr *ngFor="let item of array[i].item; let dateValue of array[i].datesArray; let h of array[i].hours">
<td>{{h}}</td>
<td>{{dateValue}}</td>
<td>{{value.projectCodeInput}}</td>
</tr>
</tbody>
</table>
I am using multiple arrays on single tag in ngFor like this:
{*ngFor="let item of array[i].item; let dateValue of array[i].datesArray; let h of array[i].hours"}
I know this is wrong way, but somehow the hours prints in the output in both places overriding the dateValue in second column.
Is there a way to print the values from hours, dateValue array in the same element(TABLE)?

Instead of having multiple inputs to the *ngFor directive, you could move the let i=index to the inner loop and use it to get the values.
I assume all the sub-arrays will always be of equal length i.e. hours, datesArray and item array will always be of equal length.
If you wish to span the projectCodeInput property across multiple rows since it'll be the same for each element of the parent array, you could do so using [attr.rowspan] property and first local variable of the *ngFor directive. The check is to make sure the span element is rendered only once for the loop.
<table class="table table-bordered">
<thead class="thead-dark">
<tr>
<th>Hours</th>
<th>Dates</th>
<th>Project Codes</th>
</tr>
</thead>
<tbody class="tbody" *ngFor="let element of arr">
<tr *ngFor="let item of element.item; let i=index; let f=first">
<td>{{element.hours[i]}}</td>
<td>{{element.datesArray[i]}}</td>
<td *ngIf="f" [attr.rowspan]="element.item.length">{{element.projectCodeInput}}</td>
</tr>
</tbody>
</table>
Working example: Stackblitz

You could use the index of the inner loop and get the values with this index z:
<table class="table table-bordered">
<thead class="thead-dark">
<tr>
<th>Hours</th>
<th>Dates</th>
<th>Project Codes</th>
</tr>
</thead>
<tbody class="tbody" *ngFor="let value of array; let i = index">
<tr *ngFor="let item of array[i].item; let z = index">
<td>{{array[i].hours[z]}}</td>
<td>{{array[i].datesArray[z]}}</td>
<td>{{value.projectCodeInput}}</td>
</tr>
</tbody>
</table>

Related

How to iterate through an array of objects in angular

I have an array booksArr of objects of class book.
book.ts class
export class Book{
bookId:number;
bookName:string;
cost:number;
quantity:number;
constructor(bookId, bookType, cost, quantity){
this.cost=cost;
this.bookId=bookId;
this.bookName=bookName;
this.quantity=quantity;
}}
booksArr in books-list.component.ts
booksArr: book[] = [
new book(100, "The Alchemist", 20, 1),
new book(101, "Rich Dad Poor Dad", 50, 2),
new book(102, "Charolett's Web", 10, 1),
new book(103, "Harry Potter", 70, 4),
new book(104, "Gone Girl", 150, 3),
];
I want to create a table in html to display the details of these books.
books-list.component.html
<table border="1" *ngIf="bName">
<thead>
<tr>
<th>book ID</th>
<th>book Name</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let b of booksArr">
<td *ngIf="b.//WHAT SHOULD I PUT HERE"</td>
</tr>
</tbody>
</table>
You are already iterating through the booksArr.just need to use interpolation to display the data. Try like this
<table border="1" *ngIf="bName">
<thead>
<tr>
<th>book ID</th>
<th>book Name</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let b of booksArr">
<td>{{b.bookId}}</td>
<td>{{b.bookName}}</td>
</tr>
</tbody>
</table>
You are already iterating the array of books with *ngFor="let b of booksArr".
You want to now interpolate the values from each book in the array. When you are inside the array, you have access to the loop variable you declare in *ngFor. In this case the loop variable is b. You can now bind to the properties on b using curly brace interpolation. {{b.bookId}}.
<table border="1" *ngIf="bName">
<thead>
<tr>
<th>book ID</th>
<th>book Name</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let b of booksArr">
<td>{{b.bookId}}</td>
<td>{{b.bookName}}</td>
</tr>
</tbody>
</table>
Try:
<td> {{ b.bookId }} </td>
<td> {{ b.bookName }} </td>
No *ngIf is required in this case. Just try the following
<tbody>
<tr *ngFor="let b of booksArr">
<td>{{b.bookId}}</td>
<td>{{b.bookName}}</td>
</tr>
</tbody>
And in your .ts file you should either change
constructor(bookId, bookName, cost, quantity)
Or
this.bookName=bookType;
inside your constructor

Create Angular2 Table Header with *ngFor loop

So, my problem is so basic but i cant solve it.
I'm trying to create dynamic table header with *ngFor.
<table>
<tr>
<th>Entry Warehouse</th>
<th colspan="2" *ngFor="let data of datas">
SomeText
</th>
</tr>
<tr>
<th>More Text</th>
<div *ngFor="let data of datas">
<th>A little text again</th>
<th>A little text again</th>
</div>
</tr>
</table>
Anyway, this solution suicide themself at that point. If datas length more than 1, div tag is underscoring th tag in same cell.
If i try another solution like this;
<table>
<tr>
<th>Entry Warehouse</th>
<th colspan="2" *ngFor="let data of datas">
SomeText
</th>
</tr>
<tr>
<th>More Text</th>
<th *ngFor="let data of datas">A little text again</th>
<th *ngFor="let data of datas">A little text again</th>
</tr>
</table>
it looks like works but actually not. Because at this time the next th tag doesnt start before the previous loop ends.
In angular 2+ you can use<ng-container> tags
<ng-conatiner *ngFor="let i of items">
<th>i</th>
</ng-conatiner>

angular 2 dynamic columns data table

Currently i have a data table with hard coded column headers and filling in data.. I would like to change this table to make it dynamic so the user can pick the columns to build the table. I would like to know how or in what way i will have to change my json object to ensure dynamic column data table creation.
This is what i have tried but the data is not being loaded.
<table>
<thead>
<tr>
<th *ngFor="let col of columnArray">{{col}}</th>
</tr>
</thead>
<table>
<tbody>
<tr *ngFor="let col of columnArray">
<td *ngFor="let data of columnData"> {{col.data}} </td>
</tr>
</tbody>
Currently since my data for the table comes from one object with hard coded headers here is my current working object:
data = [ {'id': 'idValue', 'name': 'nameValue', 'date': 'dateValue', 'description': 'descriptionValue'}, ...
]
but since i don't know what columns the user will pick to create the table it may be columns: id, name, description. Or columns: id, name. I need to have the data flexible so that when the user picks which ever columns to display in a table
Working format of the data:
columnArray = [ {'header': 'headerValue', 'data': 'dataValue'}, ...
]
Then the template can be:
<table>
<thead>
<tr><th *ngFor="let col of columnArray">{{col.header}}></th></tr>
</thead>
<tbody>
<tr>
<td *ngFor="let col of columnArray"> {{col.data}} </td>
</tr>
</tbody>
</table>
If you can provide the data format more apt solution can be provided.
EDIT#1:
Based on your data format, I'd extract keys from an object in your data array for headers.
headers = Object.keys(data[0]);
Then the html should be:
<table>
<thead>
<tr><th *ngFor="let col of headers">{{col}}></th></tr>
</thead>
<tbody>
<tr *ngFor="let obj of data">
<td *ngFor="let col of headers"> {{obj[col]}} </td>
</tr>
</tbody>
</table>

html table comes offset; most likely due to *ngFor

I'm trying to align the html table correctly but it comes off. The challenge that I have is with respect to the inner loop (modification) which is a list inside of Revision (in other words Revision 'has a' modification list.
While the result on screen are correct, the table is completely off. I speculate the problem is in the 2 *ngFor loop. Any pointer?
<table class="table table-striped">
<thead>
<tr>
<th>Revision No</th>
<th>Date</th>
<th>Username</th>
<th>Field</th>
<th>Old Value</th>
<th>New Value</th>
</tr>
</thead>
<tbody>
<tr>
<div *ngFor="let r of revisions">
<div *ngFor="let m of r.modifications">
<td>{{r.revision}}</a></td>
<td>{{r.date}}</td>
<td>{{r.username}}</td>
<td>{{m.forItem}}<td>
<td>{{m.oldInfo}}<td>
<td>{{m.newInfo}}</td>
</div>
</div>
</tr>
</tbody>
</table>
Nothing can go between your tr and td. Put the first ngfor in a tbody wrapped around your tr. Then put your second ngfor on the tr
You can't have a <div> at that position inside a table
Use
<ng-container *ngFor="let r of revisions">
instead of
<div *ngFor="let r of revisions">

Vertical table with dynamic data

Seems to be the same requirement like AngularJS "Vertical" ng-repeat but solution doesn't work for *ngFor
I have this object array that I am trying to bind to an HTML table. Its format is something like below:
[
{
"Animal":"Dog",
"Country":"France",
"Food":"Tofu",
"Car":"Nano",
"Language":"TypeScript"
}
]
Now this can simply be formatted in the default HTML horizontal table way like this:
<table>
<tr>
<th>Animal</th>
<th>Country</th>
<th>Food</th>
<th>Car</th>
<th>Language</th>
</tr>
<tr *ngFor="let data of datas">
<td>{{data.Animal}}</td>
<td>{{data.Country}}</td>
<td>{{data.Food}}</td>
<td>{{data.Car}}</td>
<td>{{data.Language}}</td>
</tr>
</table>
This would create table like below(Please ignore the data in the table;its just to give an idea.):
But how would I create a structure like this with dynamic data(kind of a vertical table):
In Component:
this.arrayOfKeys = Object.keys(this.datas[0]);
html:
<table>
<tr *ngFor="let key of arrayOfKeys">
<th>{{key}}</th>
<td *ngFor="let data of datas">
{{data[key]}}
</td>
</tr>
</table>
Use ng-container if You have data structure similar to groups[].items[].name
So, if You want to add row
<table>
<ng-container *ngFor="let group of groups">
<tr>
<td>{{group.name}}</td>
</tr>
<tr *ngFor="let item of group.items">
<td>{{item.name}}</td>
</tr>
</ng-container>
</table>
Source: https://stackoverflow.com/a/44086855/1840185