Reputation: 672
When I hit an api to get data from server in constructor and store it in local variable. I loop this variable in html to build table and show data in table. But when I call a function on api success and in it I loop the same variable which is used in HTML, the loop in function will run first and after that HTML loop. My requirement is to run HTML loop first and after its completion loop in function.
HTML CODE
<table>
<tr *ngFor="let booking of jobHistory>
<td>{{booking}}</td>
</tr>
</table>
TYPESCRIPT CODE
jobHistory : any;
constructor()
{
this.service.getCustomerJobHistory().subscribe( res => { this.jobHistory = res},
err => console.log(err),
() => this.setRating()
);
}
setRating()
{
//I want to run this loop after HTML loop completion.
for(let booking of this.jobHistory)
{
console.log(booking);
}
}
Upvotes: 2
Views: 3416
Reputation: 841
<table>
<tr *ngFor="let booking of jobHistory; let last = last">
<td>{{booking}}</td>
<ng-container *ngIf="last">{{setRating()}}</ng-container>
</tr>
</table>
setRating() {
//I want to run this loop after HTML loop completion.
for(let booking of this.jobHistory) {
console.log(booking);
}
}
The above solution will detect the last iteration of the ngFor
loop. setRating
will be called if the last index
is not null
.
Try this solution and let me know, if you need any help.
Edit: To avoid multiple execution problems,
lastValue:any;
setRating(last:any) {
if (this.lastValue==last)
return;
//I want to run this loop after HTML loop completion.
for(let booking of this.jobHistory) {
console.log(booking);
}
this.lastValue=last
}
Upvotes: 2
Reputation: 8165
So i saw the answers (including the accepted one) and while they are definitely worth knowing, i don't think it's the best way to handle your situation. The reasons already came up in the comments to the accepted answer:
When angular isn't run in prodMode
you will get weird behaviour due to angulars double powered change detection if not run in prodMode
. (Initializing and every change (even a single click on one of your elements) will your function twice due to change detection.
Even in prodMode
depending on where your array
is initialized, the function could be called multiple times. For example initializing your array like this:
export class YourComp {
jobHistory: any[] = ['one', 'two', 'three' ];
setRating() { ... }
}
will call the function three times after it's initialized, even in prodMode
(which is very weird...).
Every triggerd event, will trigger the change detection
which will end in reevaluating the last
property and therefore call your function again. So clicking on one of your elements (even though you didn't even setup a click listener) will call setRating
again.
Because of this, i suggest to try another approach.
The probably easiest way i came up with would be to use another component
.
Something like this:
export class MyTableComponent {
@Input() data: any[];
ngAfterViewInit() {
this._setRating();
}
private _setRating() {
console.log('set rating...');
}
}
As its template it should have the same table you posted in your question. In your parent component you would initialize it with something like <my-table [data]="jobHistory" *ngIf="jobHistory"></my-table>
If your jobHistory
data will be updated in your main component, you just clear it (which will destruct the MyTableComponent
), push your new / updated elements into the array again, which will reinitialize the component and finally call setRating
.
If setRating
is called too early, a little setTimeout(() => { this._setRating(); });
would do.
Hope i could help.
Upvotes: 2
Reputation: 648
Basically, you could use the 'last' operator of *ngFor
<table>
<tr *ngFor="let booking of jobHistory; let l=last">
<td>{{booking}} {{ l ? setRatings() : null}}</td>
</tr>
</table>
Upvotes: 3