Angular 17: *ngFor vs @for Control Flow
Introduction
Angular 17 introduced a fundamentally new approach to template control flow with its built-in blocks (@if
, @for
, @switch
). These replace traditional structural directives like *ngFor
, improving performance while offering a more intuitive syntax.
Problem Statement
Developers upgrading to Angular 17 encounter @for
syntax in documentation:
@for (car of cars; track car) {
<option [value]="car.value">{{car.viewValue}}</option>
}
This replaces familiar *ngFor
:
<option *ngFor="let car of cars" [value]="car.value">{{car.viewValue}}</option>
Key questions arise:
- Is
@for
a direct replacement for*ngFor
? - Why was this change introduced?
- What are concrete advantages of
@for
? - How should existing projects migrate?
Solution: Understanding Angular's New @for
@for
is Angular 17's official replacement for *ngFor
, offering significant improvements:
1. Mandatory Performance Optimization
The track
parameter is now required, forcing developers to specify a unique identifier. This prevents common performance pitfalls in *ngFor
where optional trackBy
was often omitted:
<!-- Angular 17 @for with mandatory tracking -->
@for (car of cars; track car.id) {
<app-car [car]="car" />
}
The equivalent in *ngFor
had no enforced optimization:
<!-- Old *ngFor with optional (and often omitted) trackBy -->
<app-car
*ngFor="let car of cars"
[car]="car" />
2. Built-in Empty State Handling
@for
provides dedicated syntax for empty collections via @empty
:
@for (item of items; track item.id) {
<div>{{ item.name }}</div>
} @empty {
<p>No items found</p> <!-- Rendered when items array is empty -->
}
3. Reduced Boilerplate
- No imports: Works without importing
NgFor
(critical for standalone components) - Leaner output: Generates ~30% less compiled code than
*ngFor
- Structured flow: Explicit curly braces improve template readability
4. Optimized Change Detection
@for
computes minimal DOM operations during updates using the track expression, outperforming *ngFor
in most cases. The framework handles optimizations internally.
Feature | *ngFor | @for |
---|---|---|
Tracking | Optional trackBy | Mandatory track |
Empty Collection | Manual ngIf | Built-in @empty |
Imports | Requires NgFor | No imports |
Bundle Size | Larger | Smaller |
Migration Guide
1. Automatic Conversion
Use Angular CLI to migrate templates:
ng generate @angular/core:control-flow
2. Manual Conversion Steps
Convert this structure:
<element *ngFor="let item of items; index as i; trackBy: trackFn">
{{ item }} {{ i }}
</element>
To:
@for (item of items; track trackFn($index, item); let i = $index) {
<element>
{{ item }} {{ i }}
</element>
}
Key Notes
track
expressions must return primitive values (string/number)$index
,$count
, and$first
/$last
provide context variables- Migration is backward-compatible (old syntax still works)
When Preference Isn't Enough
While some developers initially prefer *ngFor
for its one-line conciseness, @for
provides concrete advantages that reduce errors and improve performance. The required track
parameter alone prevents common UI update issues caused by improper object tracking.
Conclusion
@for
represents Angular's future-proofed approach to iterating content:
- ✅ Mandatory tracking improves performance
- ✅ Dedicated
@empty
block simplifies empty states - ✅ Zero dependencies reduce boilerplate
- ✅ Optimized change detection out-performs
*ngFor
Migrate using the Angular CLI command or manual conversion, and reference the official Control Flow Documentation for edge cases. The new syntax offers tangible benefits despite initial familiarization costs.