AcademyAngular
Control Flow: @if and @for
Angular 17+ replaced *ngIf and *ngFor with built-in @if and @for blocks — faster, stricter about tracking, and no CommonModule import required.
2 min readUpdated 3 exercises
@if replaces *ngIf, with a real else branch
The new block syntax is a template primitive, not a structural directive, so it needs no * prefix and no CommonModule import:
@if (status === 'success') {
<span class="badge badge-ok">Ready</span>
} @else if (status === 'error') {
<span class="badge badge-error">Failed</span>
} @else {
<span class="badge badge-pending">Pending</span>
}A three-state badge like this used to need either a nested *ngIf/else chain or an [ngSwitch]; @if/@else if/@else reads as what it is, and the compiler enforces that branches are mutually exclusive.
@for requires track, and track is not optional styling
@for replaces *ngFor, and its track expression is mandatory — omitting it is a compile error, not a lint warning:
@for (todo of visibleTodos; track todo.id) {
<li [class.done]="todo.done">{{ todo.text }}</li>
} @empty {
<li class="empty">Nothing matches.</li>
}track tells Angular which DOM node maps to which data item across re-renders. Tracking by todo.id means reordering the array reorders the existing <li> elements; tracking by $index (or forgetting a stable key entirely, the way *ngFor let you) means Angular matches by position instead of identity, so filtering the list drops and recreates elements it should have kept — and if one of those elements owns focus, as a search input does, position-based tracking loses that focus on every keystroke because a "different" element now occupies the same slot.
@switch replaces ngSwitch the same way
@switch (value) { @case (a) {...} @case (b) {...} @default {...} } mirrors @if's improvements over its structural-directive predecessor for the same reason: it is parsed at compile time, so a typo in a @case value is caught before runtime rather than silently matching nothing.
What interviewers check
Whether you reach for @if/@for by default in anything Angular 17+, whether you can explain why track exists and what breaks without a stable one, and whether you have actually hit the "state disappears while typing" bug that a wrong track expression produces — it is the single most common Angular list bug in production code, not just a textbook warning.
Now practice it
1 quiz · 2 drillsReading this page is the cheap half. These are the exercises that make you use it.
- Conditional RenderingWire up a three-state status badge with @if/@else if/@else, and fix a search panel that loses focus on every toggle.DrillSolve · EasyDrillSolve · Easy
- List RenderingBuild a filterable todo list where the visible count and the rendered items both actually follow the underlying data.DrillSolve · EasyDrillSolve · Easy
- Fundamentals · Templates & BindingTemplates and binding questions at the level where @if/@for edge cases start to matter for real.QuizTake · MediumQuizTake · Medium
Read next
Interpolation and Property Binding
{{ }} renders text; [prop] sets a DOM property directly. The same distinction as textContent vs. a property assignment, and it decides which one a template needs.
2 min
Event Binding and Custom Outputs
(event)="handler($event)" binds a native DOM event; @Output() lets a component emit its own, typed the same way any other value in the app is typed.
2 min