execute a function when *ngFor finished in angular 2
Solution 1:
Update
You can use @ViewChildren
for that purpose
There are three cases
1. on initialization ngFor
element is not rendred due to ngIf
on it or it's parent
- in which case, whenver
ngIf
becomes truthy, you will be notified by theViewChildren
subscription
2. on initialization ngFor
element is rendred regardless of ngIf
on it or it's parent
- in which case
ViewChildren
subscription will not notify you for the first time, but you can be sure it's rendered in thengAfterViewInit
hook
3. items are added/removed to/from the ngFor
Array
- in which case also
ViewChildren
subscription will notify you
[Plunker Demo] (see console log there)
@Component({
selector: 'my-app',
template: `
<ul *ngIf="!isHidden">
<li #allTheseThings *ngFor="let i of items; let last = last">{{i}}</li>
</ul>
<br>
<button (click)="items.push('another')">Add Another</button>
<button (click)="isHidden = !isHidden">{{isHidden ? 'Show' : 'Hide'}}</button>
`,
})
export class App {
items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
@ViewChildren('allTheseThings') things: QueryList < any > ;
ngAfterViewInit() {
this.things.changes.subscribe(t => {
this.ngForRendered();
})
}
ngForRendered() {
console.log('NgFor is Rendered');
}
}
Original
You can do it like this ( but see the side Effects for yourself )
<ul>
<li *ngFor="let i of items; let last = last">{{i}} {{last ? callFunction(i) : ''}}</li>
</ul>
Which is Useless, unless used with changeDetectionStrategy.OnPush
Then you can have control over how many times change detection occurs, hence how many times your function is called.
i.e: You can trigger next changeDetection
when the data of items
changes, and your function will give proper indication that ngFor
is rendered for real change.
Solution 2:
I used a slight hack of the approach others have complained about and it worked like a charm:
<ul>
<li *ngFor="let i of items; let last = last">{{i}} {{last ? callFunction(i) : ''}}</li>
</ul>
Then in your component:
shouldDoIt = true; // initialize it to true for the first run
callFunction(stuff) {
if (this.shouldDoIt) {
// Do all the things with the stuff
this.shouldDoIt = false; // set it to false until you need to trigger again
}
}
This doesn't address the root of the problem with this approach but it's an extremely low-cost hack that made my app run smoothly. I do hope the Angular team will implement a more friendly way to trigger some code after the *ngFor loads its content.
Solution 3:
you can do the same by getting last index using #last
of *ngFor
and call function by getting last index value and do your stuff whatever you want. here is code for the same -
<ul>
<li *ngFor="#item of items; #last = last">
<span *ngIf='last'>{{hello(last)}}</span>
{{item}}
</li>
</ul>
items: Array<number> = [1,2,3,4,5]
constructor() { console.clear();}
hello(a){
if(a==true)
this.callbackFunction();
}
callbackFunction(){
console.log("last element");
}
working example for the same Working Plunker
Solution 4:
I ran into this problem as well. In my case I was calling a web service to retrieve data and needed to execute javascript to initialize each template produced by *ngFor
. Here's what I came up with:
updateData() {
this.dataService.getData().subscribe(
function(data) {
this.data = data;
setTimeout(javascriptInitializationFunction, 0);
},
function(err) { /* handle it */ }
);
}
setTimout
will wait for the DOM To be updated before executing. This isn't exactly what you asked, but hopefully it helps.