Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

*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.

like image 877
outrowender Avatar asked Apr 18 '19 03:04

outrowender


People also ask

Can I call function in ngFor?

Do it in code and assign it to each item and then just use it inside *ngFor . Such function calls from template bindings are discouraged.

What is * ngFor directive used for?

The *ngFor directive is used to repeat a portion of HTML template once per each item from an iterable list (Collection). The ngFor is an Angular structural directive and is similar to ngRepeat in AngularJS. Some local variables like Index, First, Last, odd and even are exported by *ngFor directive.

What is * in ngFor Angular?

*ngFor is a predefined directive in Angular. It accepts an array to iterate data over atemplate to replicate the template with different data. It's the same as the forEach() method in JavaScript, which also iterates over an array.

Can we use for loop in Angular?

The for–in loop is for looping over object properties. The for–of loop is for looping over the values in an array. for–of is not just for arrays. It also works on most array-like objects including the new Set and Map types which we will cover in the next lecture.


1 Answers

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

  • https://angular.io/guide/template-syntax#no-visible-side-effects
  • Angular very weird bug on a custom element
like image 54
yurzui Avatar answered Oct 04 '22 15:10

yurzui