40

In Angular 1 I have written a custom directive ("repeater-ready") to use with ng-repeat to invoke a callback method when the iteration has been completed:

if ($scope.$last === true)
{
    $timeout(() =>
    {
        $scope.$parent.$parent.$eval(someCallbackMethod);
    });
}

Usage in markup:

<li ng-repeat="item in vm.Items track by item.Identifier"
    repeater-ready="vm.CallThisWhenNgRepeatHasFinished()">

How can I achieve a similar functionality with ngFor in Angular 2?

Tobias Punke
  • 730
  • 1
  • 7
  • 15
  • 1
    I'm having difficulty imagining when this would be useful. Why do you want to do this? Maybe there's another way to solve your real problem. – Douglas Mar 05 '16 at 20:23
  • 1
    This might help, https://angular.io/docs/ts/latest/api/common/NgFor-directive.html -- "NgFor provides several exported values that can be aliased to local variables:" one of them is "last". But I agree that it sounds like the wrong solution. – Dave Bush Mar 05 '16 at 20:39
  • 1
    The reason is a custom directive for a dropdown using Sematic-UI. I have to invoke a method from the Semantic API do make the input a dropdown, but this must be done **after** ngFor has looped through all elements. – Tobias Punke Mar 06 '16 at 00:23
  • Hello, Tobias, have you solved this problem somehow? We have same problems, when i need to initialize scrollbars after new items will appear in ngFor list. – prespic Apr 01 '16 at 11:10
  • Same problem here... – Marek Jan 15 '18 at 18:02
  • 1
    Possible duplicate of [execute a function when \*ngFor finished in angular 2](https://stackoverflow.com/questions/37087864/execute-a-function-when-ngfor-finished-in-angular-2) – Al-Mothafar Jun 16 '19 at 12:10

8 Answers8

77

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

Abhijit Jagtap
  • 2,740
  • 2
  • 29
  • 43
22

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();
  }
Sasxa
  • 40,334
  • 16
  • 88
  • 102
  • this is not worked as all other say that , template parsing erro for using in
  • – Mohammad Mirzaeyan Jun 06 '18 at 22:41
  • There's a more complete answer here: https://stackoverflow.com/a/37088348/1574059 – danguilherme Jun 12 '18 at 12:23
  • template parse error, m using it on select - option – Sunil Garg Apr 18 '19 at 14:02
  • fine solution:) – Dmitry Sobolevsky Jan 10 '20 at 11:12