@if and @for
Real templates need to show and hide content based on conditions, and render lists of items. Angular provides @if and @for — built-in control flow syntax that handles both cases cleanly.
@if — conditional rendering
Section titled “@if — conditional rendering”@if renders a block of template only when the condition is true:
@if (loading) { <div class="spinner">Loading...</div>} @else if (error) { <p class="error">{{ error }}</p>} @else { <div class="grid"> <!-- movie cards here --> </div>}This is much cleaner than the older *ngIf directive syntax:
<!-- old way — still works but not preferred --><div *ngIf="loading; else content">Loading...</div><ng-template #content>...</ng-template>The @if syntax reads like familiar TypeScript/JavaScript code, without special attribute syntax or ng-template placeholders.
@else and @else if
Section titled “@else and @else if”@if (user) { <p>Welcome, {{ user.name }}!</p>} @else { <a routerLink="/login">Sign in</a>}@if (status === 'loading') { <p>Loading...</p>} @else if (status === 'error') { <p>Something went wrong.</p>} @else { <p>Data loaded!</p>}@for — list rendering
Section titled “@for — list rendering”@for iterates over an array and renders a block for each item. The track expression is required — it tells Angular how to identify each item so it can update the DOM efficiently when the list changes:
@for (movie of movies; track movie.id) { <app-movie-card [movie]="movie" />}Using track movie.id means Angular can add, remove, or reorder individual cards without re-rendering the entire list.
If items do not have a unique identifier, you can use track $index (the loop index), but this is less efficient for list updates.
@empty — when the list is empty
Section titled “@empty — when the list is empty”@for supports an @empty block that renders when the array has no items:
@for (movie of watchlist; track movie.id) { <app-movie-card [movie]="movie" />} @empty { <p class="empty-state">Your watchlist is empty. Browse movies to add some.</p>}In CinemaVault, the Watchlist page uses this pattern to show a helpful message when no movies have been saved.
Using @if and @for together
Section titled “Using @if and @for together”@if (loading) { <div class="spinner"></div>} @else { @for (movie of movies; track movie.id) { <app-movie-card [movie]="movie" /> } @empty { <p>No movies found.</p> }}This pattern appears on every page in CinemaVault: show a loading state while the API request is in flight, then show the results (or an empty state) when it completes.
The older *ngIf and *ngFor directives
Section titled “The older *ngIf and *ngFor directives”You may encounter the older directive syntax in existing code:
<div *ngIf="condition">...</div><li *ngFor="let item of items; trackBy: trackById">{{ item.name }}</li>These still work in Angular 17+. The new @if/@for syntax is a language-level feature rather than a directive, which makes it easier to use and slightly more performant. Prefer the new syntax in all new code.
Exercise
Section titled “Exercise”- Add a
movies: string[] = ['Inception', 'Interstellar', 'Dunkirk']property to a practice component. - Add
loading = falseand a button that toggles it. - Use
@if (loading)to show a “Loading…” message, and@elseto render the list. - Inside the
@else, use@for (movie of movies; track movie)to display each title in a<li>. - Add an
@emptyblock that shows “No movies yet.” - Test by clearing the array in the constructor and confirming the empty state appears.
@if (condition) { } @else { }conditionally renders template blocks — reads like JavaScript.@for (item of list; track item.id) { }renders a block for each item in an array.trackis required — it tells Angular how to identify items for efficient DOM updates.@empty { }inside@forrenders when the array is empty.- Prefer
@if/@forover the older*ngIf/*ngFordirectives in new code.