Angular 2: Callback when ngFor has finished

For me works in Angular2 using Typescript.

<li *ngFor="let item in Items; let last = last">
  ...
  <span *ngIf="last">{{ngForCallback()}}</span>
</li>

Then you can handle using this function

public ngForCallback() {
  ...
}

You can use @ViewChildren for that purpose

@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.ngForRendred();
    })
  }

  ngForRendred() {
    console.log('NgFor is Rendered');
  }
}

origional Answer is here https://stackoverflow.com/a/37088348/5700401


The solution is quite trivial. If you need to know when ngFor completes printing all the DOM elements to the browser window, do the following:

1. Add a placeholder

Add a placeholder for the content being printed:

<div *ngIf="!contentPrinted">Rendering content...</div>

2. Add a container

Create a container with display: none for the content. When all items are printed, do display: block. contentPrinted is a component flag property, which defaults to false:

<ul [class.visible]="contentPrinted"> ...items </ul>

3. Create a callback method

Add onContentPrinted() to the component, which disables itself after ngFor completes:

onContentPrinted() { this.contentPrinted = true; this.changeDetector.detectChanges(); }

And don't forget to use ChangeDetectorRef to avoid ExpressionChangedAfterItHasBeenCheckedError.

4. Use ngFor last value

Declare last variable on ngFor. Use it inside li to run a method when this item is the last one:

<li *ngFor="let item of items; let last = last"> ... <ng-container *ngIf="last && !contentPrinted"> {{ onContentPrinted() }} </ng-container> <li>

  • Use contentPrinted component flag property to run onContentPrinted() only once.
  • Use ng-container to make no impact on the layout.

You can use something like this (ngFor local variables):

<li *ngFor="#item in Items; #last = last" [ready]="last ? false : true">

Then you can Intercept input property changes with a setter

  @Input()
  set ready(isReady: boolean) {
    if (isReady) someCallbackMethod();
  }