Built-in Directives

Chapter 4
Built-in Directives
Conditional rendering and lists directly in the template — Angular's answer to JSX's ternaries and .map()

React handles conditionals and lists with plain JavaScript embedded in JSX — ternaries, &&, and .map() (Fundamentals Chapters 5 and 6). Angular instead provides directives: special attributes recognized by the template compiler that add, remove, or repeat elements. This chapter covers the modern built-in control-flow syntax (@if, @for) plus the attribute directives for classes and styles.

@if — Conditional Rendering

@if (isLoggedIn) { <p>Welcome back!</p> } @else { <p>Please log in.</p> }

The @if block (introduced in Angular 17 as the modern built-in control flow) renders its contents only when the condition is true, with an optional @else block — the direct equivalent of React's ternary or &&, but reading more like the if/else statement it actually is. Unlike React's conditional rendering, which keeps the element in the JSX expression, @if genuinely adds or removes the element from the DOM entirely based on the condition.

@if/@for replaced *ngIf/*ngFor
Older Angular code (and most tutorials written before late 2023) uses the structural directives *ngIf="condition" and *ngFor="let x of items" as attributes on an element. They still work, but the newer @if/@for block syntax is now the recommended default — cleaner to read, and built into the template compiler rather than requiring CommonModule to be imported. This course uses the modern block syntax throughout; recognize the older *ng forms when you see them in existing code.

@for — Rendering a List

<ul> @for (item of items; track item.id) { <li>{{ item.name }}</li> } </ul>

@for repeats its block once per array item — the equivalent of React's .map(). The track item.id clause is required, and serves exactly the same purpose as React's key prop (Fundamentals Chapter 6): it tells Angular how to identify each item across re-renders so it can update the DOM efficiently when the list changes, rather than rebuilding everything. Where React's key is optional-but-warned, Angular's track is mandatory.

@empty and the Loop Variables

@for (todo of todos; track todo.id) { <li>{{ todo.text }}</li> } @empty { <li>No todos yet.</li> }

An optional @empty block renders when the array has no items — neatly handling the "empty list" case that React's Todo project (Project 1) had to write as a separate conditional. Inside a @for, contextual variables like $index, $first, $last, and $even are also available for free — {{ $index }} gives the current item's position, for instance.

@switch — Multiple Cases

@switch (status) { @case ('loading') { <p>Loading...</p> } @case ('error') { <p>Something went wrong.</p> } @default { <p>Ready.</p> } }

@switch handles three-or-more named cases cleanly — the same job React's lookup-object pattern did (Fundamentals Chapter 5), expressed as template-level branching. This is a natural fit for the loading/error/success status pattern that ran throughout the React projects.

ngClass and ngStyle — Multiple Classes or Styles at Once

<div [ngClass]="{ active: isActive, disabled: isDisabled }">...</div> <div [ngStyle]="{ color: textColor, 'font-size.px': size }">...</div>

Chapter 3's [class.active] toggles one class; [ngClass] takes an object toggling several at once, each key a class name and each value a boolean deciding whether it applies. [ngStyle] does the same for multiple inline styles. Both require NgClass/NgStyle (or CommonModule) in the component's imports — unlike the new @ control-flow blocks, these attribute directives still need importing.

@if removes from the DOM — it doesn't just hide
A @if that's false removes its element from the DOM completely, which also destroys any component inside it (and its state). If an element only needs to be visually hidden while keeping its state alive, a plain [style.display] or [hidden] binding is the right tool instead — @if is for genuinely adding/removing, not merely showing/hiding.
AngularReact equivalent
@if (x) { } @else { }Ternary / &&
@for (x of items; track x.id) { }items.map(...) + key
@empty { }A separate "empty list" conditional
@switch / @caseLookup object for many states
[ngClass]="{ ... }"Conditional className

Coding Challenges

Challenge 1

Build a component with a boolean isLoggedIn property toggled by a button, using @if/@else to show either "Welcome back!" or "Please log in." accordingly.

📄 View solution
Challenge 2

Given an array of objects ({ id, name }), render them as a list with @for (using track on the id), including an @empty block showing "No items" when the array is cleared.

📄 View solution
Challenge 3

Build a component with a status property ("loading"/"error"/"success") cycled by a button, using @switch to render different content per status, and apply [ngClass] to color the message based on the same status.

📄 View solution

Chapter 4 Quick Reference

  • @if / @else — conditional rendering; genuinely adds/removes the element from the DOM
  • @for (x of items; track x.id) — list rendering; track is mandatory (React's key equivalent)
  • @empty — renders when the array is empty; $index/$first/$last/$even available inside @for
  • @switch / @case / @default — clean branching for three-or-more named cases
  • [ngClass] / [ngStyle] — toggle several classes/styles at once via an object (needs importing)
  • The modern @ blocks replaced the older *ngIf/*ngFor structural directives
  • Next chapter: component communication — @Input, @Output, and EventEmitter