Ad

*ngFor in Angular



    If you are an Angular developer, you must be pretty familiar with the *ngFor directive (if you haven't know about it yet, take a look at this article ). It is a core structural directive that loops through every element of a collection and renders the predefined template for each of them. So all we need is just tell ngFor which array to use, and define what we want to show for each item. Let's look at an example:
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-ngfor-example',
  templateUrl: './ngfor-example.component.html',
})
export class NgForExampleComponent implements OnInit {
  people: any[] = [
    { name: "Wiliams", age: 16 },
    { name: "Ellise", age: 23 },
    { name: "Thomas", age: 20 },
    { name: "Phillip", age: 35 },
    { name: "David", age: 18 },
  ];

  constructor() { }

  ngOnInit(): void {
  }
}
    
    Here we have an array of people along with their information, and we want to display it as a list to the users.
<ul>
  <li *ngFor="let person of people">
    - Name: {{ person.name }}, Age: {{person.age}}
  </li>
</ul>
    
    With the expression let person of people , where people is the array we want to display and person is the variable we are defining as a reference to the current array element so that we can access the property name and age of the current object, after being executed, the generated template will look just like this:
<ul>
  <li>- Name: Wiliams, Age: 16</li>
  <li>- Name: Ellise, Age: 23</li>
  <li>- Name: Thomas, Age: 20</li>
  <li>- Name: Phillip, Age: 35</li>
  <li>- Name: David, Age: 18</li>
</ul>
    
    It is import to know that the variable we defined in the expression of *ngFor can only be accessed inside the HTML element containing the directive and its children. So these below line of codes will not work as we expected:
<ul>
  <li *ngFor="let person of people"> </li>
  - Name: {{ person.name }}, Age: {{person.age}}
</ul>
    
    To get the index of each element in the array, we can define another variable in *ngFor directive as a value of the index:
<ul>
  <li *ngFor="let person of people; let i = index">
    {{i+1}}. Name: {{ person.name }}, Age: {{person.age}}
  </li>
</ul>

    And now we can get the current index of every item inside the template via i variable.
We can also check if an element is the first or the last item of current collection, or its index is an odd or an even number by using first, last, even, odd values exported by *ngFor directive.
<ul>
  <li *ngFor="let person of people;
      let first = first; let last = last; let even = even; let odd = odd">
  </li>
</ul>
  • first: true if current item is the first element of the collection.
  • last: true if current item is the last element of the collection.
  • odd: true if current item has an odd index in the collection.
  • even: true if current item has an even index in the collection
    
    Now, we're going to do something. Let's add a button, and when the button is clicked, we will add a new person into the array.
<ul>
  <li *ngFor="let person of people; let i = index">
    {{ i + 1 }} - {{ person.name }}
  </li>
</ul>
<button (click)="addPerson()"></button>
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-ngfor-example',
  templateUrl: './ngfor-example.component.html',
})
export class NgForExampleComponent implements OnInit {
  people: any[] = [
    { name: "Wiliams", age: 16 },
    { name: "Ellise", age: 23 },
    { name: "Thomas", age: 20 },
    { name: "Phillip", age: 35 },
    { name: "David", age: 18 },
  ];

  constructor() { }

  ngOnInit(): void {
  }

  addPerson() {
    this.people.push({ name: "Hiroto", age: 28 });
  }
}
    
    So, we can see, after the button is clicked, the list on the screen immediately changes and adds our new item to the bottom. But how can the *ngFor know that the collection has changed? It uses something called Change Detection. Basically, Angular can detect when component data changes, and the re-render the view automatically (I will talked about this more clearly in another part). In this case, which is *ngFor directive, it tracks the collection by reference. So whenever a new reference is passed to *ngFor, specifically:
  • When a new item is added
  • When an item is removed
  • When items are reordered
    Angular will automatically clear the whole old DOM and rebuild the new one for new collection. With a small collection, that may be not a big deal, but for a huge list, like thousands of rows, it is an very expensive price for the performance. To avoid this, angular provide us a way to track which items are changed and which are not, so that Angular can only update the DOM elements associated with the items affected by the change. All we need to do is help Angular with the identification of each item by providing trackBy function. This function has to return a property causes an object to be unique.
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-ngfor-example',
  templateUrl: './ngfor-example.component.html',
})
export class NgForExampleComponent implements OnInit {
  people: any[] = [
    { id: 1, name: "Wiliams", age: 16 },
    { id: 2, name: "Ellise", age: 23 },
    { id: 3, name: "Thomas", age: 20 },
    { id: 4, name: "Phillip", age: 35 },
    { id: 5, name: "David", age: 18 },
  ];

  constructor() { }

  ngOnInit(): void {
  }

  trackByFn(index, item) {
    return item ? item.id : null
  }
}
    
    In this case, the property to identify each object is id, so we have to return it in the trackByFn and tell Angular it is the function it should use to track the element.
<ul>
  <li *ngFor="let person of people; trackBy: trackByFn">
    - Name: {{ person.name }}, Age: {{person.age}}
  </li>
</ul>

So, we have discovered the power of *ngFor directive and how it help when we develop an Angular project. Hope you find this section useful.