Chapter 4.5☕ 16 min read

Old Syntax (*ngIf *ngFor) vs New (@if @for) Migration

Angular 17+ introduced cleaner @ syntax — here's how to migrate from the old * syntax.

01Why Angular Changed the Syntax

Angular 17+ changed how structural directives work — from *ngIf and *ngFor to @if and @for.

"Purana syntax = magic show — samajh nahi aata kya ho raha hai."

"Naya syntax = transparent glass — sab dikhta hai."

Why the change?

  • Hidden complexity: The * prefix expanded to <ng-template> under the hood — beginners had no idea this was happening
  • No else support: *ngIf had no native else — you needed ng-template with #elseRef
  • No @empty: *ngFor had no built-in empty state
  • No @switch: No built-in switch directive existed
  • New @ syntax: Consistent block syntax that matches JavaScript
02@if vs *ngIf — Complete Migration

Old: *ngIf

<!-- Simple condition -->\n<div *ngIf="isVisible">Hello</div>\n<div *ngIf="!isVisible">Bye</div>

New: @if

@if (isVisible) {\n  <div>Hello</div>\n} @else {\n  <div>Bye</div>\n}

Old: *ngIf with ng-template else

<div *ngIf="role === 'admin'; else guestBlock">Admin</div>\n<ng-template #guestBlock><div>Guest</div></ng-template>

New: @if with @else — no reference variables needed!

@if (role === 'admin') {\n  <div>Admin</div>\n} @else {\n  <div>Guest</div>\n}

"Else template reference variable hat gaya — seedha @else likho."

03@for vs *ngFor — Complete Migration

Old: *ngFor

<li *ngFor="let item of items; trackBy: trackFn">\n  {{ item.name }}\n</li>

New: @for

@for (item of items; track item.id) {\n  <li>{{ item.name }}</li>\n} @empty {\n  <li>No items</li>\n}

Key differences:

  • track is REQUIRED — was optional with *ngFor (trackBy). Angular now forces you to think about performance.
  • trackBy function → direct expressiontrackBy: trackFn becomes track item.id. Much simpler!
  • @empty block — replaces *ngIf="items.length === 0" wrapper
  • Index: let i = index in *ngFor vs let i = $index in @for

"Track mandatory hai — Angular force karta hai best practice."

04@switch — Brand New

@switch — this is completely new. There was no built-in switch directive before!

Old way: Had to use chained @if/@else if or the NgSwitch directive with *ngSwitchCase:

// Old: chained @if (before @else if existed)\n@if (status === 'pending') { <p>Waiting</p> }\n@if (status === 'preparing') { <p>Cooking</p> }\n@if (status === 'done') { <p>Complete</p> }\n\n// Old: NgSwitch directive\n<div [ngSwitch]="status">\n  <div *ngSwitchCase="'pending'">Waiting</div>\n  <div *ngSwitchDefault>Unknown</div>\n</div>

New way: @switch

@switch (status) {\n  @case ('pending') { <p>⏳ Waiting</p> }\n  @case ('preparing') { <p>🍳 Cooking</p> }\n  @default { <p>❓ Unknown</p> }\n}

"Pehle switch ke liye alag library lgani padti thi — ab built-in hai."

05Should You Migrate Existing Code?

Should you migrate your existing Angular code?

  • ✅ New code — Always use new @ syntax. No question.
  • ⚠️ Old Angular 16 and below projects — If it works and is stable, don't break it just for syntax.
  • ✅ Angular 17+ upgrade — The Angular CLI has a schematic: ng generate @angular/core:control-flow
  • ⚠️ Hybrid codebase — Mixing old and new is fine during migration, but aim for consistency.

"Naya kaam naya syntax, purana kaam chhod do agar chale toh."

The CLI schematic automatically converts *ngIf → @if and *ngFor → @for throughout your project. Run it once and review the changes.

Key Takeaways

  • ✅ New @ syntax replaces *ngIf, *ngFor with cleaner block syntax — no hidden ng-template.
  • ✅ @if supports @else and @else if natively — no more ng-template #elseRef.
  • ✅ @for requires track (mandatory) and supports @empty block — better performance by design.
  • ✅ @switch is brand new — no built-in switch existed in older Angular.
  • ✅ Use Angular CLI schematic ng g @angular/core:control-flow to auto-migrate.