Search code examples
angularloopsngforangular2-changedetection

How does Angular2+ ngFor directive iterate over the ref iterable?


I try to understand how the *ngFor directive of Angular works behind the scene. Something I wanted to know was if the iterable passed to the directive was interpreted at each loop iteration (like the condition of the JS for loop) or if angular creates a "cache"-like variable with that value. To answer this question, I did a ngFor that has a method returning an array as the iterable object.

<li *ngFor="let data of test(dataArr)">{{data}}</li>

The related TS :

 dataArr = [1,2,3,4];
 iterationNb = 1;
 test(arr) {
    console.log('has iterated', this.iterationNb++);
    return arr.filter(i => true);
 }

And here what I get in the console :

has iterated 1
has iterated 2
has iterated 3
has iterated 4

If you need the angular project, here is my example app used for my tryouts : https://stackblitz.com/edit/angular-3kmyyw

That's great, I see that angular just call the method at each iteration. But that's where it starts to be complex for me...

I then tried to change the ref array dataArr used by the method by adding some other values in it like :

dataArr = [1,2,3,4,5,6,7];

But now I still have in my console 4 iterations... Then I tried with

dataArr = [1,2];

and booom still 4 iterations...

So now I'm very confused about this... Why do I have always 4 iteration ? The first attempt was just luck of having choosen 4 items in my array and in fact it has nothing to do with that ? I tried to look at the angular ngfor source code but I'm still not that ninja that understands it without explanations...

Thanks


Solution

  • That's great, I see that angular just call the method at each iteration. But that's where it starts to be complex for me...

    You are not seeing iterations. Angular called the method each time the DOM was rendered. It has nothing to do with how many elements were in the array. It was just by luck you saw it count to 4.

    <li *ngFor="let data of test(dataArr)">
    

    The above is executed once per rendering pass. The *ngFor directive breaks down the expression into two variables. A reference to the current iterable, and a reference to the source iterator.

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators#Iterators

    ngFor only needs to assign the source iterator once per rendering pass. In your expression the test() function returns the iterator (which happens to be an array). It will take the next() value from the iterator and assign it to the data variable which will be exposed in the template.

    ngFor will keep calling next() until it reaches the end of the iterator. For each iteration ngFor checks if there is a DOM element that should be inserted, updated or removed. It knows what data goes with which DOM element using a trackBy function.

    https://medium.com/@ramy_ali/improving-angular-ngfor-performance-through-trackby-ae4cf943b878