Search code examples
angulartypescriptngfortemplatebinding

*ngFor using a function, returns a loop


When i use *ngFor in angular with a function returning my data, the function is called multiple times, and sometimes resulting even in a loop:

app.component.ts

export class AppComponent {

 getArray(): string[] {

   //here i know when this function is called
   console.log('getArray called')

   return ['number one', 'number two']
 }

}

app.component.html

<h1 *ngFor="let item of getArray()">
 {{ item }}
</h1>

My console:

enter image description here

Then i get the function getArray() called multiple times and i dont know why.


Solution

  • Update

    You see that it's called multiply time because Angular evaluates all expressions you're using in your template on every change detection cycle. The change detection cycle starts with ApplicationRef.tick method.

    When application starts it calls that tick method immediately and then it's managed by ngZone.onMicrotaskEmpty subscription.

    Additionaly, every tick method executes additional check checkNoChanges for dev mode.

    So you're getting

    App starts
       loadComponent
          tick
            checkChanges
                  evaluate getArray()
            checkNoChanges
                  evaluate getArray()
      ngZone.onMicrotaskEmpty
          subscribe(all promised have been executed)
             tick
               checkChanges
                  evaluate getArray()
               checkNoChanges
                  evaluate getArray()
    
          ...some time later
          subscribe(you click somewhere)
             tick
               checkChanges
                  evaluate getArray()
               checkNoChanges
                  evaluate getArray()
          subscribe(you make a http request)
             tick
               checkChanges
                  evaluate getArray()
               checkNoChanges
                  evaluate getArray()
    

    Previous answer

    You should avoid using expressions in Angular template that execute complex calcultation or perform side effect or return new value on every change detection run.

    Particularly in your code

    <h1 *ngFor="let item of getArray()">
    

    you're returning a new array on every template check. And ngForOf directive detects that you changed array and tries to rerender it(if your items would be an objects).

    It's better if you define that array once in your code.

    arr = ['number one', 'number two']
    
    <h1 *ngFor="let item of arr">
    

    Another way that can work for ngForOf directive is using trackBy but it would better to have some unique key in item for that.

    See also