Angular @for: Complete Guide

Chidume Nnamdi 🔥💻🎵🎮
7 min readMar 17, 2024

Introduction

With the release of Angular 17, it came as a game changer since the advent of the Angular framework. It is the @for control syntax.

In this blog post, we will learn everything about the @for syntax.

I found most of the sources I used for this article on daily.dev, it’s a great browser extension that lets you set up a personal news feed, and has a lot of great Angular content. Here’s a link to the angular posts on daily.dev — https://app.daily.dev/search?q=angular

Read on!

@for syntax

The @for control syntax is a control syntax built into the Angular templating engine.

It is used to repeatedly render elements in the DOM for each item in a collection.

The @for is built-in so there is no need to import any module for us to use it.

The @for syntax is this:

@for(item of items; track item) {

}

  • The @for tells the Angular templating engine that we want to repeat an element in the DOM.
  • item holds the current item in the iteration
  • items is the collection/iterable to be iterated over. E.g array, string, etc
  • track denotes the identity to pass to each element.

Now, let’s see a basic example:

@Component({

template: `

@for(fruit of fruits; track fruit) {

<div>{{ fruit }}</div>

}

`,

})

export class FruitComponent {

fruits = [“apple”, “lemon”];

}

Here, we have a fruits member field, which is an array.

We used the @for syntax to render all the fruits in the array in the DOM.

The fruit template variable holds the current fruit in the iteration.

The track fruit gives identity to each DOM in the @for. So we are telling Angular to identify each rendered DOM by the current fruit name.

This track fruit enables Angular to track each DOM, and also knows when and how to update the DOM when any of the DOMs is updated, removed, or added. With the track, Angular cannot simply re-render all the items for a single change, it uses the identity to calculate particular DOMs to update.

This boosts the performance of our apps because our lists will not be re-rendered from scratch when a change occurs.

When in doubt about which identity to use, use track $index.

See that the @for has a striking resemblance with the native for…of statement:

for (let fruit of fruits) {

console.log(fruit);

}

@for with @empty

There might be a scenario when there are no elements to be rendered in the collection or we can just say, the collection is empty.

@Component({

template: `

@for(fruit of fruits; track fruit) {

<div>{{ fruit }}</div>

}

`,

})

export class FruitComponent {

fruits = [];

}

This will display an empty DOM.

For a good UI/UX experience, we need to display a message on the DOM telling the user that the fruits array is empty.

To do that, we use the @empty syntax:

@Component({

template: `

@for(fruit of fruits; track fruit) {

<div>{{ fruit }}</div>

} @empty {

<div>No fruits</div>

}

`,

})

export class FruitComponent {

fruits = [];

}

The <div>No fruits</div> will be displayed because the fruits array is empty.

@for with String

We can pass a string to the @for syntax. All the characters of the string will be iterated over.

This is because the String is iterable in JavaScript.

The String class has the Symbol.iterator on it.

str[Symbol.iterator];

It will return:

ƒ [Symbol.iterator]() { [native code] }

This tells us that String is iterable.

Anything iterable by the native JavaScript for…of can also be iterated over by the @for syntax.

Let’s see how we can iterate over a string using the @for.

@Component({

template: `

@for(character of name; track character) {

<div>{{ character }}</div>

}

`,

})

export class FruitComponent {

name = “nnamdi”;

}

In the DOM, we will have:

<div>n</div>

<div>n</div>

<div>a</div>

<div>m</div>

<div>d</div>

<div>i</div>

@for with Iterable objects

Like I said previously, @for works with iterables.

Iterables are:

  • Arrays
  • Strings
  • Sets
  • Maps
  • Arguments object

Sets

@Component({

template: `

@for(set of mySet; track set) {

<div>{{ set }}</div>

}

`,

})

export class FruitComponent {

mySet = new Set([1, 2, 3, 4, 5]);

}

Maps

@Component({

template: `

@for(map of myMap; track map) {

<div>{{ map }}</div>

}

`,

})

export class FruitComponent {

myMap = new Map([

[“key1”, “value1”],

[“key2”, “value2”],

[“key3”, “value3”],

]);

}

Arguments object

@Component({

template: `

@for(arg of getArgs(“nna”); track arg) {

<div>{{ arg }}</div>

}

`,

})

export class FruitComponent {

getArgs(arg: string) {

return arguments;

}

}

@for and Object

We can’t use an object with @for:

@Component({

template: `

@for(prop of user; track prop) {

<div>{{ prop }}</div>

}

`,

})

export class FruitComponent {

user = {

name: “Anna”,

};

}

It will throw an error:

NG8: Type ‘{ name: string; }’ must have a ‘[Symbol.iterator]()’ method that returns an iterator.

To iterate over the user object we need to make it an iterable. This is done by declaring a ‘[Symbol.iterator]()’ in it.

When we declare a [Symbol.iterator] method in the user object, we will make it return the value of the name property.

Let’s see how it’s done:

@Component({

template: `

@for(prop of user; track prop) {

<div>{{ prop }}</div>

}

`,

})

export class FruitComponent {

user = {

name: “Anna”,

[Symbol.iterator]: function* () {

yield this.name;

},

};

}

See, we added the [Symbol.iterator] method to the user object and see that it is a generator function. It yields the value of its name property.

Generator functions are defined with the asterisk *. They do not return a value traditionally using the return keyword, rather they yields a certain value each time they are called.

So generator functions are suited for use in [Symbol.iterator] method, this is because the [Symbol.iterator] method is called on each iteration of a collection to get the values till the last. So the generator functions do just that.

The above component will display:

<div>Anna</div>

We can modify the [Symbol.iterator] method in the user object to return each character in the user.name property value.

@Component({

template: `

@for(prop of user; track prop) {

<div>{{ prop }}</div>

}

`,

})

export class FruitComponent {

user = {

name: “Anna”,

[Symbol.iterator]: function* () {

yield this.name[0];

yield this.name[1];

yield this.name[2];

yield this.name[3];

},

};

}

This will display:

<div>A</div>

<div>n</div>

<div>n</div>

<div>a</div>

To make the [Symbol.iterator] dynamic:

@Component({

template: `

@for(prop of user; track prop) {

<div>{{ prop }}</div>

}

`,

})

export class FruitComponent {

user = {

name: “Anna”,

[Symbol.iterator]: function* () {

for (let i = 0; i < this.name.length; i++) {

yield this.name[i];

}

},

};

}

Works the same.

@for with $index

The $index is a contextual variable used to get the current index in the iteration. Let’s see how we use it.

@Component({

template: `

@for(fruit of fruits; track fruit; let index = $index) {

<div>{{ fruit }} {{ index }}</div>

}

`,

})

export class FruitComponent {

fruits = [“apple”, “lemon”];

}

See that to get the index, we added ; after the track fruit, then we set let index = $index.

The code grabs the value of the current index from the $index and stores it in the index template variable. This variable is only visible with the current @for scope.

See, we then rendered it in between the @for braces {}.

Our DOM will be this:

<div> apple 0 </div>

<div> lemon 1 </div>

@for with $first and $last

The $first and $last contextual variables are boolean that indicate if the current iteration is the first iteration or the last iteration.

If it is the first iteration, then $first holds true while $last holds false. If the current iteration is neither the first nor the last, then they are all false. If the current iteration is the last in the iteration, then the $first is false, while $last is true.

@Component({

template: `

@for(fruit of fruits; track fruit; let first = $first, last = $last) {

<div>First: {{ first }}: Last {{ last }}</div>

}

`,

})

export class FruitComponent {

fruits = [“apple”, “lemon”];

}

DOM:

<div>First: true: Last false</div>

<div>First: false: Last true</div>

@for with $odd and $even

The $odd and $even contextual variables are booleans that indicate if the index value of the current iteration is either an odd number or an even number respectively.

@Component({

template: `

@for(fruit of fruits; track fruit; let even = $even, odd = $odd) {

<div>Even: {{ even }}: Odd {{ odd }}</div>

}

`,

})

export class FruitComponent {

fruits = [“apple”, “lemon”];

}

DOM:

<div>Even: true: Odd false</div>

<div>Even: false: Odd true</div>

@for with $count

The $count contextual variable is the number of elements iterated over:

@Component({

template: `

@for(fruit of fruits; track fruit; let count = $count) {

<div>Count: {{ count }}</div>

}

`,

})

export class FruitComponent {

fruits = [“apple”, “lemon”];

}

The DOM will be this:

<div>Count: 2</div>

<div>Count: 2</div>

@for vs *ngFor

@for

  • @for is less verbose
  • @for is easy to understand
  • No imports needed
  • track functionality is a must
  • brings performance right out of the package.
  • @for is tied in with the Angular templating engine.

ngFor

  • ngFor is complex
  • Needs to be imported, it is a stand-alone directive
  • Needs a weird asterisk symbol to be appended before the ngFor.
  • track functionality is optional

Migrating to @for

To migrate your Angular project use the @for control flow.

Run this command:

ng generate @angular/core:control-flow

Summary

We started by introducing the @for syntax.

Next, we saw basically how to use the @for control flow syntax.

Then, we saw how to render iterables using the @for control flow syntax, and how to use the contextual variables.

@for is definitely a game-changer in the Angular world.

--

--

Chidume Nnamdi 🔥💻🎵🎮

JS | Blockchain dev | Author of “Understanding JavaScript” and “Array Methods in JavaScript” - https://app.gumroad.com/chidumennamdi 📕