Angular ngFor Example
June 20, 2020
This page will walk through Angular ngFor
example. Angular provides NgForOf
directive with ngFor
selector. It instantiates a template for every element of given iterator. The NgForOf
has local variables that can be used in iteration. The local variables are index, first, last, even, odd. The ngFor
is used with HTML elements as well as <ng-template>
. Whenever the contents of iterator changes, ngFor
performs respective changes in DOM. These changes are tracked by object identity by default. We can change tracking identify by using trackBy
. We assign a user defined function to trackBy
and that function will return an identity for every element of iterator. When we use trackBy
with ngFor
, it starts change propagation tracked by given identity and not by object identity. Using trackBy
improves the performance of ngFor
directive. If iterator is the instance of Observable
or Promise
, we need to use async
pipe with ngFor
directive. Here on this page we will provide complete example of ngFor
with its local variables and trackBy
function step by step.
Contents
Technologies Used
Find the technologies being used in our example.1. Angular 9.1.11
2. Node.js 12.5.0
3. NPM 6.9.0
NgForOf Directive
TheNgForOf
directive instantiates a template for every item in an iteration. The selector of NgForOf
is ngFor
.
According to Angular Doc, NgForOf
directive has been defined as following.
@Directive({ selector: '[ngFor][ngForOf]' }) class NgForOf<T> implements DoCheck, OnChanges { ------- }
NgForOf
directive.
index: Index of current item.
even: True for an even index.
odd: True for an odd index.
first: True for first item.
last: True for last item.
ngForOf: It is useful to alias when expression is more complex than a property access, for example using Async pipe such as (obsPersons | async) .
NgFor with HTML Elements
ThengFor
directive is used with HTML elements as following.
<li *ngFor="let item of items; index as i; even as isEven; odd as isOdd; first as isFirst; last as isLast; trackBy: trackByFn"> ------ </li>
NgFor with <ng-template>
ThengFor
directive is used with <ng-template>
as following.
<ng-template ngFor let-item [ngForOf]="items" let-i="index" let-isEven="even" let-isOdd="odd" let-isFirst="first" let-isLast="last" [ngForTrackBy]="trackByFn"> <li> ------ </li> </ng-template>
index, even and odd
Here we will usengFor
with index
, even
and odd
local variables using <div>
element.
<div *ngFor="let person of allPersons; index as i; even as isEven; odd as isOdd"> <font color="blue" *ngIf="isEven">{{i + 1}}: {{person.personId}} - {{person.name}} - {{person.age}} </font> <font color="red" *ngIf="isOdd">{{i + 1}}: {{person.personId}} - {{person.name}} - {{person.age}} </font> <input [(ngModel)]="person.name"> <button (click)="remove(person.personId)">Remove</button> </div>
<ng-template>
as following.
<ng-template ngFor let-person [ngForOf]="allPersons" let-i="index" let-isEven="even" let-isOdd="odd"> <div> <font color="blue" *ngIf="isEven">{{i + 1}}: {{person.personId}} - {{person.name}} - {{person.age}} </font> <font color="red" *ngIf="isOdd">{{i + 1}}: {{person.personId}} - {{person.name}} - {{person.age}} </font> <input [(ngModel)]="person.name"> <button (click)="remove(person.personId)">Remove</button> </div> </ng-template>
first and last
Find the use offirst
and last
local variables of ngFor
using <div>
element.
<div *ngFor="let person of allPersons; index as i; first as isFirst; last as isLast"> <font color="blue" *ngIf="isFirst; else elseBlock1">{{i + 1}}: {{person.personId}} - {{person.name}} - {{person.age}} </font> <ng-template #elseBlock1> <font color="red" *ngIf="isLast; else elseBlock2">{{i + 1}}: {{person.personId}} - {{person.name}} - {{person.age}} </font> <ng-template #elseBlock2> {{i + 1}}: {{person.personId}} - {{person.name}} - {{person.age}} </ng-template> </ng-template> <input [(ngModel)]="person.name"> <button (click)="remove(person.personId)">Remove</button> </div>
<ng-template>
as following.
<ng-template ngFor let-person [ngForOf]="allPersons" let-i="index" let-isFirst="first" let-isLast="last"> <div> <font color="blue" *ngIf="isFirst; else elseBlock1">{{i + 1}}: {{person.personId}} - {{person.name}} - {{person.age}} </font> <ng-template #elseBlock1> <font color="red" *ngIf="isLast; else elseBlock2">{{i + 1}}: {{person.personId}} - {{person.name}} - {{person.age}} </font> <ng-template #elseBlock2> {{i + 1}}: {{person.personId}} - {{person.name}} - {{person.age}} </ng-template> </ng-template> <input [(ngModel)]="person.name"> <button (click)="remove(person.personId)">Remove</button> </div> </ng-template>
NgFor with Async Pipe
Here we will useasync
pipe with ngFor
using <li>
element. Suppose we have an instance of Observable
as following.
obsPersons: Observable<Person[]>
<li *ngFor="let person of obsPersons | async"> -- </li>
<li *ngFor="let person of obsPersons | async as persons">
ngFor
with async
pipe with aliasing using <li>
element.
<ul> <li *ngFor="let person of obsPersons | async as persons; index as i"> {{i + 1}} out of {{persons.length}}: {{person.personId}} - {{person.name}} - {{person.age}} <input [(ngModel)]="person.name"> <button (click)="remove(person.personId)">Remove</button> </li> </ul>
ngForOf
with <ng-template>
for async
pipe.
<ng-template ngFor let-person [ngForOf]="obsPersons | async"> <li> --- </li> </ng-template>
ngForOf
with <ng-template>
is achieved as let-persons="ngForOf"
. Find the code snippet.
<ng-template ngFor let-person [ngForOf]="obsPersons | async" let-persons="ngForOf"> <li> --- </li> </ng-template>
ngForOf
with async
pipe with aliasing using <li>
element.
<ul> <ng-template ngFor let-person [ngForOf]="obsPersons | async" let-persons="ngForOf" let-i="index"> <li> {{i + 1}} out of {{persons.length}}: {{person.personId}} - {{person.name}} - {{person.age}} <input [(ngModel)]="person.name"> <button (click)="remove(person.personId)">Remove</button> </li> </ng-template> </ul>
trackBy
ThengFor
uses trackBy
for change propagation. When the contents of the iterator changes, ngFor
makes the corresponding changes to DOM as given below.
1. If an item is added then new instance of template is added in DOM.
2. If an item is removed then its respective template is removed from DOM.
3. If items are reordered, respective templates are reordered in DOM.
4. If no change for an item, its respective template in DOM will be unchanged.
Angular uses object identity to maintain the entire above task of change propagation. If we want that Angular should not use object identity for change propagation but use user provided identity, then we can achieve it using
trackBy
. It assigns a function that accepts index
and item
as function arguments. We can use trackBy
with HTML elements as following.
<li *ngFor="let person of allPersons; trackBy: trackByFn"> -- </li>
trackByFn
can be defined to return an identity as following.
trackByFn(index: number, item: any) { // return any id }
<li>
element.
<ul> <li *ngFor="let person of allPersons; index as i; trackBy: personTrackByFn"> {{i + 1}}: {{person.personId}} - {{person.name}} - {{person.age}} <input [(ngModel)]="person.name"> <button (click)="remove(person.personId)">Remove</button> </li> </ul>
personTrackByFn
to return person id.
personTrackByFn(index: number, person: Person) { return person.personId; }
<ng-template>
then the same can be achieved using ngForTrackBy
. Find the code snippet.
<ul> <ng-template ngFor let-person [ngForOf]="allPersons" let-i="index" [ngForTrackBy]="personTrackByFn"> <li> {{i + 1}}: {{person.personId}} - {{person.name}} - {{person.age}} <input [(ngModel)]="person.name"> <button (click)="remove(person.personId)">Remove</button> </li> </ng-template> </ul>
trackBy
or ngForTrackBy
to change the default behavior (i.e. change propagation by object identity). Suppose a scenario that iterator of ngFor
is produced from RPC to server and that RPC is re-run then object identity may change. So ngFor
will re-draw the template even as there is no change in any object value. That impacts the performance and hence in this case we should use trackBy
or ngForTrackBy
function to change the default tracking id.
Complete Example
Project Structureangular-demo | |--src | | | |--app | | | | | |--person.ts | | |--person.service.ts | | |--ngforof-demo.component.ts | | |--ngforof-demo.component.html | | |--ng-template-ngforof.component.ts | | |--ng-template-ngforof.component.html | | | | | |--app.module.ts | | |--app.component.ts | | | |--main.ts | |--index.html | |--styles.css | |--node_modules |--package.json
export class Person { constructor(public personId:number, public name:string, public age:number) { } }
import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { Person } from './person'; @Injectable() export class PersonService { PERSONS = [ new Person(101, 'Mahesh', 25), new Person(102, 'Ram', 20), new Person(103, 'Krishna', 30), new Person(104, 'Bharat', 25) ]; getAllPersons(): Observable<Person[]> { return Observable.of(this.PERSONS); } add(name: string, age: number) { let maxIndex = this.PERSONS.length - 1; let objWithMaxIndex = this.PERSONS[maxIndex]; let newId = objWithMaxIndex.personId + 1; this.PERSONS.push(new Person(newId, name, age)); } remove(personId: number) { let obj = this.PERSONS.find(ob => ob.personId === personId); this.PERSONS.splice(this.PERSONS.indexOf(obj), 1); } }
import { Component, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { PersonService } from './person.service'; import { Person } from './person'; @Component({ selector: 'ngforof-demo', templateUrl: './ngforof-demo.component.html' }) export class NgForOfDemoComponent implements OnInit { name: string; age: number; allPersons: Person[]; obsPersons: Observable<Person[]> constructor(private personService: PersonService) { } ngOnInit() { this.personService.getAllPersons() .subscribe(persons => this.allPersons = persons); this.obsPersons = this.personService.getAllPersons(); } add() { this.personService.add(this.name, this.age); this.name = ''; this.age = undefined; } remove(personId: number) { this.personService.remove(personId); } personTrackByFn(index: number, person: Person) { return person.personId; } }
<div> <p><b>Add Person</b></p> <p>Name: <input [(ngModel)]="name">, Age: <input [(ngModel)]="age"> </p> </div> <button (click)="add()">ADD</button> <br/><b>Aliasing index, even, odd </b> <br/><br/> <div *ngFor="let person of allPersons; index as i; even as isEven; odd as isOdd"> <font color="blue" *ngIf="isEven">{{i + 1}}: {{person.personId}} - {{person.name}} - {{person.age}} </font> <font color="red" *ngIf="isOdd">{{i + 1}}: {{person.personId}} - {{person.name}} - {{person.age}} </font> <input [(ngModel)]="person.name"> <button (click)="remove(person.personId)">Remove</button> </div> <br/><b>Aliasing first, last </b> <br/><br/> <div *ngFor="let person of allPersons; index as i; first as isFirst; last as isLast"> <font color="blue" *ngIf="isFirst; else elseBlock1">{{i + 1}}: {{person.personId}} - {{person.name}} - {{person.age}} </font> <ng-template #elseBlock1> <font color="red" *ngIf="isLast; else elseBlock2">{{i + 1}}: {{person.personId}} - {{person.name}} - {{person.age}} </font> <ng-template #elseBlock2> {{i + 1}}: {{person.personId}} - {{person.name}} - {{person.age}} </ng-template> </ng-template> <input [(ngModel)]="person.name"> <button (click)="remove(person.personId)">Remove</button> </div> <b>Aliasing ngForOf </b> <ul> <li *ngFor="let person of obsPersons | async as persons; index as i"> {{i + 1}} out of {{persons.length}}: {{person.personId}} - {{person.name}} - {{person.age}} <input [(ngModel)]="person.name"> <button (click)="remove(person.personId)">Remove</button> </li> </ul> <br/><b>Using trackBy </b> <ul> <li *ngFor="let person of allPersons; index as i; trackBy: personTrackByFn"> {{i + 1}}: {{person.personId}} - {{person.name}} - {{person.age}} <input [(ngModel)]="person.name"> <button (click)="remove(person.personId)">Remove</button> </li> </ul>
import { Component, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { PersonService } from './person.service'; import { Person } from './person'; @Component({ selector: 'ng-template-ngforof', templateUrl: './ng-template-ngforof.component.html' }) export class NgTemplateNgForOfComponent implements OnInit { name: string; age: number; allPersons: Person[]; obsPersons: Observable<Person[]> constructor(private personService: PersonService) { } ngOnInit() { this.personService.getAllPersons() .subscribe(persons => this.allPersons = persons); this.obsPersons = this.personService.getAllPersons(); } add() { this.personService.add(this.name, this.age); this.name = ''; this.age = undefined; } remove(personId: number) { this.personService.remove(personId); } personTrackByFn(index: number, person: Person) { return person.personId; } }
<div> <p><b>Add Person</b></p> <p>Name: <input [(ngModel)]="name">, Age: <input [(ngModel)]="age"> </p> </div> <button (click)="add()">ADD</button> <br/><b>Aliasing index, even, odd </b> <br/><br/> <ng-template ngFor let-person [ngForOf]="allPersons" let-i="index" let-isEven="even" let-isOdd="odd"> <div> <font color="blue" *ngIf="isEven">{{i + 1}}: {{person.personId}} - {{person.name}} - {{person.age}} </font> <font color="red" *ngIf="isOdd">{{i + 1}}: {{person.personId}} - {{person.name}} - {{person.age}} </font> <input [(ngModel)]="person.name"> <button (click)="remove(person.personId)">Remove</button> </div> </ng-template> <br/><b>Aliasing first, last </b> <br/><br/> <ng-template ngFor let-person [ngForOf]="allPersons" let-i="index" let-isFirst="first" let-isLast="last"> <div> <font color="blue" *ngIf="isFirst; else elseBlock1">{{i + 1}}: {{person.personId}} - {{person.name}} - {{person.age}} </font> <ng-template #elseBlock1> <font color="red" *ngIf="isLast; else elseBlock2">{{i + 1}}: {{person.personId}} - {{person.name}} - {{person.age}} </font> <ng-template #elseBlock2> {{i + 1}}: {{person.personId}} - {{person.name}} - {{person.age}} </ng-template> </ng-template> <input [(ngModel)]="person.name"> <button (click)="remove(person.personId)">Remove</button> </div> </ng-template> <b>Aliasing ngForOf </b> <ul> <ng-template ngFor let-person [ngForOf]="obsPersons | async" let-persons="ngForOf" let-i="index"> <li> {{i + 1}} out of {{persons.length}}: {{person.personId}} - {{person.name}} - {{person.age}} <input [(ngModel)]="person.name"> <button (click)="remove(person.personId)">Remove</button> </li> </ng-template> </ul> <br/><b>Using ngForTrackBy </b> <ul> <ng-template ngFor let-person [ngForOf]="allPersons" let-i="index" [ngForTrackBy]="personTrackByFn"> <li> {{i + 1}}: {{person.personId}} - {{person.name}} - {{person.age}} <input [(ngModel)]="person.name"> <button (click)="remove(person.personId)">Remove</button> </li> </ng-template> </ul>
import { Component } from '@angular/core'; @Component({ selector: 'app-root', template: ` <ngforof-demo></ngforof-demo> <!--ng-template-ngforof></ng-template-ngforof--> ` }) export class AppComponent { }
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; import { NgForOfDemoComponent } from './ngforof-demo.component'; import { NgTemplateNgForOfComponent } from './ng-template-ngforof.component'; import { PersonService } from './person.service'; @NgModule({ imports: [ BrowserModule, FormsModule ], declarations: [ AppComponent, NgForOfDemoComponent, NgTemplateNgForOfComponent ], providers: [ PersonService ], bootstrap: [ AppComponent ] }) export class AppModule { }
Run Application
To run the application, find following steps.1. Download source code using download link given below on this page.
2. Use downloaded src in your angular CLI application. To install angular CLI, find the link.
3. Run ng serve using command prompt.
4. Now access the URL http://localhost:4200
Find the print screen of the output.

References
NgForOf DirectiveAngular <ng-template> Example