How to call a function after updating HTML content in angular2?
When i got to api to get data from server in constructor and store it in local variable. I will loop this variable in html to create the table and show the data in the table. But when I call the function on success api and in it I take the same variable used in HTML, the loop function is run first and after that HTML loop. My requirement is to start the HTML loop first and after it ends in a 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);
}
}
source to share
<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 solution above will detect the last iteration of the loop ngFor
. setRating
will be called if the latter is index
not null
.
Try this solution and let me know if you need 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
}
source to share
So, I've seen the answers (including the accepted ones) and while they are definitely noteworthy, I don't think this is the best way to handle your situation. The reasons have already come in the comments to the accepted answer:
-
When angular fails to run in
prodMode
, you get weird behavior due to double-tapped change detection when not executed inprodMode
. (Initialization and every change (even one click on one of your items) will double your function due to change detection. -
Even
prodMode
depending on where yours is initializedarray
, the function can be called multiple times. For example, initialize 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 strange ...). -
Each triggerd event fires
change detection
, which will end up re-evaluating the propertylast
and therefore calling your function again. So clicking on one of your elements (even if you haven't even set up a click listener) will trigger againsetRating
.
Because of this, I suggest trying a different approach. Probably the easiest way I've come across is to use another component
.
Something like that:
export class MyTableComponent {
@Input() data: any[];
ngAfterViewInit() {
this._setRating();
}
private _setRating() {
console.log('set rating...');
}
}
As a template, it should have the same table you posted in your question. In your parent component, you initialize it with something like<my-table [data]="jobHistory" *ngIf="jobHistory"></my-table>
If your data jobHistory
gets updated in the main component, you just clear it (which will destroy MyTableComponent
), push again on new / updated elements in the array, which will re-initialize the component and finally call setRating
.
If setRating
called too early, it may take a little setTimeout(() => { this._setRating(); });
.
Hope I can help.
source to share