How to bind data with ngFor in angular - html

I am new to angular. Just learning it.
I want to bind data with ngFor. but its not showing any data. Kindly help me to solve this
html file :
<tbody class="text-center">
<tr *ngFor="let agent of agentlist ;let i = index">
<td>{{i + 1 + paginationObject?.page * paginationObject?.itemPerPage}}</td>-->
<td>{{agent?.id?agent?.id:''}}</td>
</tr>
</tbody>
error : <!--bindings={ "ng-reflect-ng-for-of": null }-->

Try removing the --> at the end of the first table cell and correct the binding on the second one.
<tbody class="text-center">
<tr *ngFor="let agent of agentlist ;let i = index">
<td>{{i + 1 + paginationObject?.page * paginationObject?.itemPerPage}}</td>-->
<td>{{agent?.id}}</td>
</tr>
</tbody>
Also, you need to make sure that agentlist has something to iterate. maybe putting a console.log(agentlist) and see what it has inside of it in the ts class. If angelist is only declared but never defined you will get that error. Initialize angelist as an empty array to make sure that have an iterable before defining with the final data.

Related

Angular : Unable to parse Nested Json

I have the following JSON response from a web service. I want to use it on the front end but the "codeSubstance" is not showing anything. How do I parse it correctly ?
I've tried the following :
<tr *ngFor ="let m of medicamentsDetailslist;">
<td>{{m.compositions.substancesActives.codeSubstance}}</td>
</tr>
and this also
<tr *ngFor ="let m of medicamentsDetailslist;">
<td>{{m.compositions.substancesActives["codeSubstance"]}}</td>
</tr>
Any help appreciated
compositions and substancesActives are again an array, so you should be iterating over it again
<tr *ngFor ="let m of medicamentsDetailslist;">
<td>
<ng-container *ngFor="let comp of m.compositions">
<ng-container *ngFor="let sub of comp.substancesActives">
{{sub.codeSubstance}}
</ng-container>
</ng-container>
</td>
</tr>
hi i think you have an array in substancesActives, so try this :
<tr *ngFor ="let m of medicamentsDetailslist;">
<td>{{m.compositions.substancesActives[0].codeSubstance}}</td>
</tr>
If it's ok, so add a new loop for substanceActives
EDIT (more details in comments) :
m.compositions[0].substancesActives[0].codeSubstance

Filling all input fields

There is a markup:
<div class="scroll">
<table class="table table-striped">
<thead>
<tr>
<th>Image</th>
<th>Name</th>
<th>Author</th>
<th>Year</th>
<th>ISBN</th>
<th>Price</th>
<th>Count</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let book of bookService.bookList">
<td><img src="../../{{book.ImagePath}}" width="100" height="150"></td>
<td>{{book.Name}}</td>
<td>{{book.Author}}</td>
<td>{{book.Year}}</td>
<td>{{book.ISBN}}</td>
<td>{{book.Price}}</td>
<td>{{book.Count}}</td>
<td>
<input type="text" name="Count" [(ngModel)]="Count">
<button class="btn btn-block btn-outline-success" (click)="onAdd(book, Count)"><i class="fa fa-plus"></i></button>
</td>
</tr>
</tbody>
The last column looks like this:
The problem is the following: when filling one TextBox, all the TextBoxes in the column are filled.
How can I solve this problem? Tried to give unique names to text fields and to thrust this cell in form, but all the same did not work.
You need to give unique name to the input field using angular template
<input [name]="'count' + i" >
Where i is the index from the *ngFor
But I think the major issue you have is that you need to bind book.Count instead of just Count
In latter case you'll have one variable called Count and you bind the same variable to all of the input fields. You need to attach the variable to the book itself so it is unique.
All your inputs have the same [(ngModel)]="Count" and the same name so if you update one, all of them will be updated
You can fix that if you have an array of count instead. So it will be something like
<tr *ngFor="let book of bookService.bookList; let i = index">
...
<input type="text" [name]="'count' + i" [(ngModel)]="count[i]">
People are giving you the HTML way, I'm giving you the Angular way : trackBy functions.
*ngFor="let book of bookService.bookList; trackBy: book.Name"
This one should work but I've never tested it.
The one should work in any case :
*ngFor="let book of bookService.bookList; trackBy: customTB"
customTB(item, index) {
return `${index}-${item.Name}`;
}
You can't use the same name for your inputs.
To fix this, you can add an id populate by the index of the loop *ngFor
Try this :
<tr *ngFor="let book of bookService.bookList; let i = index">
<input type="text" name="Count_${i}" [(ngModel)]="count">

How to display table row data based on headers sequence from json objects in Angular 6?

I am facing problem in showing dynamic table from JSON objects. I have two array. One array contains headers of the table and other array contains JSON objects.The problem is that I want the data to be shown in sequence of header array value from each JSON Object(each object is treated as row).
The code I have tried to show data is:
<table id="myTable">
<tr >
<th *ngFor="let head of FilteredKeys;let i =index" (click)="sortTable(i)">{{head}}</th>
</tr>
<tr *ngFor="let object of finalContactsArray;">
<td *ngIf="hasProp('FilteredKeys', 'id')">{{object.id}}</td>
<td *ngIf="hasProp('FilteredKeys', 'firstname')" >{{object.firstname}}</td>
<td *ngIf="hasProp('FilteredKeys', 'lastname')">{{object.lastname}}</td>
<td *ngIf="hasProp('FilteredKeys', 'prefix')">{{object.prefix}}</td>
</tr>
</table>
Filtered Key array is:
["id","firstname","prefix","lastname"]
finalContactArray is:
[{"id":"1","firstname":"Vikas"},{"id":"2","firstname":"Raj","lastname":""},{"id":"3","prefix":"Mr.","lastname":"sdsdfsd"},{"id":"4","prefix":"Mrs."},{"id":"5","firstname":"Hari","prefix":"Mr."}]
For demo of the problem the stackblitz is:
https://stackblitz.com/edit/angular-8cacgk
Here you can see that prefix column values are shown in lastname.How to get rid of this problem?
My working code:
<table id="myTable">
<tr >
<th *ngFor="let head of FilteredKeys;let i =index" (click)="sortTable(i)">{{head}}</th>
</tr>
<tr *ngFor="let object of finalContactsArray;">
<ng-container *ngFor="let head of FilteredKeys">
<td *ngIf="head==='id' && hasProp('FilteredKeys', 'id')">{{object[head]}}</td>
</ng-container>
<ng-container *ngFor="let head of FilteredKeys">
<td *ngIf=" head === 'prefix' && hasProp('FilteredKeys', 'prefix')" (click)="showalert()" style="cursor:pointer" >{{object[head]}}</td>
</ng-container>
<ng-container *ngFor="let head of FilteredKeys">
<td *ngIf=" head === 'firstname' && hasProp('FilteredKeys', 'firstname')" >{{object[head]}}</td>
</ng-container>
<ng-container *ngFor="let head of FilteredKeys">
<td *ngIf=" head === 'lastname' && hasProp('FilteredKeys', 'lastname')">{{object[head]}}</td>
</ng-container>
</tr>
If your data structure is this simple and the relationships are that direct, you can accomplish this with simple nested for loops:
<table id="myTable">
<tr >
<th *ngFor="let head of FilteredKeys;let i =index" (click)="sortTable(i)">{{head}}</th>
</tr>
<tr *ngFor="let object of finalContactsArray;">
<td *ngFor="let key of FilteredKeys"
(click)="key === 'prefix' && showalert()"
[ngStyle]="{'cursor': (key === 'prefix') ? 'pointer': 'default'}">
{{object[key] || ' '}}
</td>
</tr>
</table>
Just iterate through your rows and within rows iterate over your header keys to show the correct row property in the cell, and create cells for every column regardless of what the actual object contains. My example puts in a single space as a placeholder if no value for that column, but you could put whatever you want, all depends what your requirement is, but you need to create cells for every column, regardless of if the object has the property or not, otherwise the cells will never align correctly.

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.

Angular 2+ : Condition based on previous index date value ngIf NgFor

Trying to format a table using Angular where:
If the day of the month changes, then you insert a new row which just contains the date (but also displays the data for that index value immediately below). If the day of the week is the same as before continue inserting the rows.
1) Clearly my code for accessing previous value in index is wrong but I can't seem to find anything clearly which helps.
2) I realise that my current code would compare the full datetime value and not just the day of the month (or week) but I am unsure how to do this in this scenario.
3) when the day changes and I try and insert the date line, I cannot get an additional new row formatted correctly which includes the data for that index value. I have tried playing around with various and combinations.
Please could someone help correct this code or point me in the right direction
Thanks
<table class="table-striped">
<thead>
<tr>
<td>day</td>
<td>time</td>
<td>region</td>
<td>event</td>
<td>period</td>
</tr>
</thead>
<tbody>
<tr *ngFor="let eco of eco_r ; let i = index">
<template [ngIf]="eco.date != eco[i-1].date">
<td colspan="5">{{eco.datetime| date:'longDate' }}</td>
<tr>
<td>{{eco.datetime | date:'EE'}}</td>
<td>{{eco.datetime | date:'shortTime'}}</td>
<td>{{eco.region}}</td>
<td>{{eco.event}}</td>
<td>{{eco.period}}</td>
</tr>
</template>
<template [ngIf]="eco.date == eco[i-1].date">
<td>{{eco.datetime | date:'EE'}}</td>
<td>{{eco.datetime | date:'shortTime'}}</td>
<td>{{eco.region}}</td>
<td>{{eco.event}}</td>
<td>{{eco.period}}</td>
</template>
</tr>
</tbody>
</table>
------------UPDATE ---------------
Following #Iancovici comments I have revised the following
i) corrected to reference eco_r[i-1].date
ii) Changed sql source to provide eco.day_of_month and eco.week_of_year to make it easier to reference and check conditions
iii) updated code to only check previous value when i > 0
I am still unable to get it to display a new line with the date AND also include the data for that value of i on a separate row where it formats correctly. Utilising as in my revised code puts all 5 values into the first column of the next row and messes put the table format. How should I resolve this please?
Thanks
<template [ngIf]="i >0 && eco.day_of_month != eco_r[i-1].day_of_month">
<tr><td colspan="5">{{eco.datetime| date:'longDate' }}</td>
</tr>
<tr> <td>{{eco.datetime | date:'EE'}}</td>
<td>{{eco.datetime | date:'shortTime'}}</td>
<td> {{eco.region}} </td>
<td>{{eco.event}}</td>
<td>{{eco.period}}</td>
</tr>
</template>
Should probbly be *ngIf not [ngIf] in general. But I see in your case it’s ok because you’re using an ng template which means instead of directive *ngIf it’s not a bonded property [ngIf]
Also you're accesing the same instance, should access index of array instead so..
change from
*ngIf="eco.date == eco[i-1].date">
to
*ngIf="eco.date == eco_r[i-1].date">
Update: Two more notes
Make sure you create a table, with tag
Don't be afraid to use paranthesis if conditonal expressions become more complex, so you can distinguish the two conditions.
Try this without the filters, then integrate the filters in.
<table>
<div *ngFor="let eco of eco_r; let i = index">
<div *ngIf="(i > 0) && (eco.day_of_month != eco_r[i-1].day_of_month)">
<tr>
<td colspan="5">{{eco.datetime }}</td>
</tr>
<tr>
<td>{{eco.datetime }}</td>
<td>{{eco.datetime }}</td>
<td> {{eco.region}} </td>
<td>{{eco.event}}</td>
<td>{{eco.period}}</td>
</tr>
</div>
</div>
</table>