nested array rendering using ngFor angular - html

I have a table where tr is repeated using *ngFor a collection, the collection which have a child members which should be binded as related table.
Note This is similar to nested array but only difference the html is not inside the nested array. I have referenced ngFor deep nested array already
typescript class
export class CreateBooking{
id: number;
bookings: BookingItem[]; // This should repeat as parent rows,
}
export class BookingItem {
id: number;
bookingNumber: number;
relatedVehicles: BookingItem[]; //This should be repeated as child rows
}
Html
<table>
<tr *ngFor="let booking of createBooking.bookings;let bookingIndex = index;">
<td>
<input type="text"
[ngModel]="booking.serialType" name="serialType--{{bookingIndex}}" />
</td>
</tr>
<tr> *ngFor="let v of booking[parent].relatedVehicles;
let i = index;
let parent = booking[something];"
<td>
<input [(ngModel)]="v.bookingNumber"
(ngModelChange)="serialNumberChangedIndex(parentIndex,$event,i)"
name="bookingNumber--{{i}}"/>
</td>
</tr>
</table>
Question 1 - How would i get the index of parent row on the child row
Question 2 - As a alternative i tried a inside which contains another table which have child items, But even with td colspan="allcolumns" i couldn't achieve expected output.
Challenges
- if i design the like this
<tr *ngFor="let booking of createBooking.bookings;let bookingIndex = index;">
<td>
<input type="text"
[ngModel]="booking.serialType" name="serialType--{{bookingIndex}}" />
</td>
<td>
<table>
<tbody>
<tr> *ngFor="let v of booking.RelatedVehicles;let i = index;"
<td>
<input [(ngModel)]="v.bookingNumber"
(ngModelChange)="serialNumberChangedIndex(bookingIndex ,$event,i)"
name="bookingNumber--{{i}}"/>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
This way i could generate the Nested array but the child collection will start at the 2nd column, I need the child element as full row.
Working sample code stackblitz
Please help me with some
expected output

From your code above, I do not see any relation in the second tag to the first one, where as your data structure I see the need for a nested tag. I think you should use a tag for the outer loop and a tag for the inner loop.

Related

How to make table with 4 cells in row, in html using angularJS

I have to make a table, where the first cell in a row is the name of the store, and other cells are names of terminals related to that store. Below you can see how it looks now. Stores is a list of stores, where every store has a list of terminals.
<tbody>
<tr *ngFor="let store of stores; let i = index">
<td>
<label>{{ store.name }}</label
>
</td>
<td *ngFor="let terminal of store.terminals">
<label>{{ terminal.name }}</label
>
</td>
</tr>
</tbody>
It's working perfectly until there aren't too many terminals. Now I need to break that row in more of them.
Now the table looks like this:
Store1 Terminal1 Terminal2 terminal3 terminal4 Terminal5 Terminal6 terminal7 terminal8
Store2 Terminal1 Terminal2 terminal3 terminal4 Terminal5 Terminal6 terminal7 terminal8
And I need to make it look like this:
Store1 Terminal1 Terminal2 terminal3 terminal4
Terminal5 Terminal6 terminal7 terminal8
Store2 Terminal1 Terminal2 terminal3 terminal4
Terminal5 Terminal6 terminal7 terminal8
I thought, that I could make another table, only for terminals, but that didn't work as I thought it would.
Instead of repeating the "td" with terminal data. Add a div inside that td and repeat the terminal data inside that.
<table>
<tbody>
<tr *ngFor="let store of stores; let i = index">
<td>
<label >{{ store.name }}</label>
</td>
<td>
<div class="terminals">
<span class="terminals_value" *ngFor="let terminal of store.terminals">{{ terminal.name }}</span></div>
</td>
</tr>
</tbody>
</table>
Add the css to style the div block as appropriate
.terminals{
width:500px;
display:flex;
flex-wrap: wrap;
}
.terminals_value{
width:100px;
padding:2px;
}
I have created a stackblitz project as a solution, you can check it out here - https://stackblitz.com/edit/table-structure

Remove last array element and return array and also return removed element separately

I have an array of objects. I have two tables on the screen. I want n-1 elements in top table and the last element to be displayed in bottom column.
HTML top table:
<th>name</th>
<th>age</th>
<tr *ngFor="let key of array">
<td> {{key.name}}</td>
<td> {{key.age}}</td>
HTML bottom table:
<th></th>
<th>age</th>
<tr *ngFor="let key of array">
<td></td>
<td> {{key.age}}</td>
suppose if there are 5 elements, 4 should be displayed at top and last at bottom. In api response I am getting a single array of objects and last object should always be in the bottom table. Also, last table does not have a name column. It returns a string "Final Name". Can this be used to detect that if name === Final Name then remove it from array and show it else where?
What should be the for loop condition for it? Should I slice last element from array and store it in temp array in .ts file?
You could create a variable to store the last array object in your ts file then pop the array to this variable. E.g:
component.ts:
export class YourComponent {
lastItem: YourType;
...
lastItem = myArray.pop();
This will remove the last item and assign it to lastItem variable which can be used to populate your bottom table.
HTML bottom table:
<th></th>
<th>age</th>
<tr>
<td></td>
<td> {{lastItem.age}}</td>
Ok first of all use the *ngFor like this
*ngFor="let item of items; let i = index"
And you can apply condition like
*ngIf="i!=array.length"
And by using index you can print those last array elements separately

Angular checkbox gets updated even though ngModel doesn't

I'm working on a shift arrangement app. In it I'm trying to create two tables that show which possible shifts each user has selected.
Both tables display the same data, but arrange it differently. Each table cell has a number of check-boxes that display the possible shifts for each person (in table 1) or the possible people for a shift (in table 2). A checkbox from table 1 that displays shift A option for person X will have the same data-bind as its equivalent checkbox in table 2, which displays person X option for shift A.
The purpose of this is to update the equivalent data in both tables simultaneously when the user couples a person with a shift. The problem: when a checkbox in table 1 is checked/unchecked, all of the check-boxes in table 2 gets checked/unchecked, as shown below:
Here is my template:
<div class="table-container" dir="ltr">
<h3>People</h3>
<table>
<thead>
<th>Name</th>
<th>Options</th>
</thead>
<tbody>
<tr *ngFor="let user of userPreferences">
<td>{{user.name}}</td>
<td>
<div *ngFor="let selection of userYesses[user.name]">
<mat-checkbox class="option-checkbox" dir="ltr" [(ngModel)]="selection.isSelected" name="usc">{{selection.option}}</mat-checkbox>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="table-container" dir="ltr">
<h3>Shifts</h3>
<table>
<thead>
<th>Time</th>
<th>Options</th>
</thead>
<tbody>
<tr *ngFor="let shift of totalShifts">
<td dir="ltr">{{shift.time}}</td>
<td>
<div *ngFor="let selection of shiftYesses[shift.time]">
<mat-checkbox class="option-checkbox" [(ngModel)]="selection.isSelected" name="syc">{{selection.name}}</mat-checkbox>
</div>
</td>
</tr>
</tbody>
</table>
</div>
And here is relevant component code:
this.userPreferences.forEach(u => {
this.userYesses[u.name] = [];
u.preferences.shifts.forEach(week => {
week.forEach(day => {
if (!day.shifts) return;
day.shifts.forEach(shift => {
if (!this.shiftYesses[`${day.date} ${shift.time}`]) this.shiftYesses[`${day.date} ${shift.time}`] = [];
if (shift.isSelected) {
let selection = new Selection(`${day.date} ${shift.time}`, u.name);
this.userYesses[u.name].push(selection);
this.shiftYesses[`${day.date} ${shift.time}`].push(selection);
}
});
});
});
});
The code seems alright to me, am I missing anything? Maybe it's a bug in Angular?
Thanks in advance!
In case anyone else experiences this issue -
After a few days of struggling with this, I stumbled upon this issue from Angular's git - https://github.com/angular/angular/issues/9230
I've read the following in kara's answer:
In the case that you don't want to register a form control, you currently have a few options:
1 - Use ngModel outside the context of a form tag. This will never throw.
<input [(ngModel)]="person.food">
After reading this, I switched the <form> tag into a <div> and everything works as expected now.

How to print a table from a 2D Array in Angular 5?

I'm trying to print a table from a 2 dimensional array of objects, holding a attribute 'text'. It only prints the table rows, iterating through the fields doesn't work.
My component.html looks like this:
<section *ngIf="object">
<table>
<tr *ngFor="let row of array; let even = even; let odd = odd"
[ngClass]="{ odd: odd, even: even }">
<td class="field" *ngFor="let field of array[row]">
{{field.text}}
</td>
</tr>
</table>
</section>
The array: object[][] is filled correctly and i can log the 'text' attributes to the console. The Problem is: I don't know how to iterate through the 2nd dimension (*ngFor="let field of array[row]")
Assuming array is an array of arrays,
Your second ngFor should be *ngFor="let field of row"
You can't use array[row] since row contains the array of the second dimension and not the index.
<section *ngIf="object">
<table>
<tr *ngFor="let row of array; let even = even; let odd = odd"
[ngClass]="{ odd: odd, even: even }">
<td class="field" *ngFor="let field of row">
{{field.text}}
</td>
</tr>
</table>
</section>
Example

Adding data into rows of next column

Basically I am creating a data grid that displays data from different tables in database.I am using ASP.NET MVC4 for this.
Following is how my part of my code looks:
<table class="myTable">
<tbody>
#foreach (var item in Model.sets)
{
<tr class="parent" data-level="0">
<td>
#item.setName
#* Here my logic would go*#
</td>
</tr>
}
So after displaying the rows of name of sets,in the next column I would want to display the elements that belong to that set.So I wrote another for loop in <td></td> that would loop through the Model.elements and check if it belongs to that set and display it.But I am getting "Element td cannot be nested within element td " validation error.
So how can I add data into rows of the next column?
Put your logic below the first end td tags instead. You can't put td tags inside other td tags; a series of sibling td tags make up a row.
If you want each separate piece of data in its own column:
#foreach (var item in Model.sets)
{
<tr class="parent" data-level="0">
<td>
#item.setName
</td>
#foreach (var element in Model.elements)
{
<td>
#*logic here*#
</td>
}
</tr>
If you want all your content inside the same cell, then you want:
#foreach (var item in Model.sets)
{
<tr class="parent" data-level="0">
<td>
#item.setName
</td>
<td>
#foreach (var element in Model.elements)
{
#*logic here*#
}
</td>
</tr>
}