Angular's New Template Syntax: @if, @for, @defer
With Angular 17 the team introduced a new template control flow system
that replaces the long-standing structural directives *ngIf, *ngFor,
and ngSwitch. The new syntax using @if, @for,
@switch, and @defer represents a fundamental shift in how
Angular templates are written: more readable, more performant, and with entirely new
capabilities like declarative lazy loading of components.
In this third article of the Modern Angular series, we will analyze each construct in depth: from basic syntax to advanced patterns, from automatic migration to community-proven best practices. If you are still using classic structural directives, this guide will show you why now is the time to migrate.
Modern Angular Series Overview
| # | Article | Focus |
|---|---|---|
| 1 | Angular Signals | Fine-grained reactivity |
| 2 | Zoneless Change Detection | Removing Zone.js |
| 3 | You are here - New Template @if, @for, @defer | Modern control flow |
| 4 | Standalone Components | NgModule-free architecture |
| 5 | Signal Forms | Reactive forms with Signals |
| 6 | SSR and Incremental Hydration | Server-Side Rendering |
| 7 | Core Web Vitals in Angular | Performance and metrics |
| 8 | Angular PWA | Progressive Web App |
| 9 | Advanced Dependency Injection | Tree-shakable DI |
| 10 | Migrating from Angular 17 to 21 | Migration guide |
Why a New Syntax? The Limitations of Structural Directives
Structural directives *ngIf, *ngFor, and ngSwitch
served Angular for nearly a decade, but they have structural limitations that the new
syntax completely resolves.
Problems with Classic Structural Directives
- Opaque microsyntax: Expressions like
*ngFor="let item of items; trackBy: trackFn; let i = index; let first = first"are hard to read and remember - No native else-if: With
*ngIfthere is noelse if: you must nest multiple*ngIfblocks or useng-templatewithelse - Bundle weight: Structural directives are Angular classes imported from CommonModule that must be included in the bundle
- Implicit track:
*ngFordoes not require atrackByfunction, causing silent performance issues - No lazy loading: There is no declarative mechanism to load components on-demand directly from the template
- Verbosity: A simple if/else requires
ng-templatewith template reference variables, increasing boilerplate
Let us look at a concrete example that shows the readability difference.
<!-- Old syntax: verbose and fragmented -->
<div *ngIf="user; else loadingTpl">
<h2>{{ user.name }}</h2>
<div *ngIf="user.role === 'admin'; else userTpl">
<span class="badge admin">Administrator</span>
</div>
<ng-template #userTpl>
<span class="badge user">User</span>
</ng-template>
</div>
<ng-template #loadingTpl>
<div class="spinner">Loading...</div>
</ng-template>
<!-- New syntax: clear and linear -->
@if (user) {
<h2>{{ user.name }}</h2>
@if (user.role === 'admin') {
<span class="badge admin">Administrator</span>
} @else {
<span class="badge user">User</span>
}
} @else {
<div class="spinner">Loading...</div>
}
New Syntax Benefits at a Glance
| Aspect | Structural Directives | New Control Flow |
|---|---|---|
| Required imports | CommonModule or individual imports | None (built into the compiler) |
| Else-if | Not natively supported | Native @else if |
| Mandatory track | No (optional) | Yes (mandatory in @for) |
| Lazy loading | Not available | @defer with declarative triggers |
| Bundle size | Directives included in bundle | Zero overhead (compiled) |
| Type checking | Limited | Complete |
| Readability | Complex microsyntax | Similar to JavaScript/TypeScript |
@if and @else: Modern Conditional Rendering
The @if block replaces *ngIf with a syntax that
closely resembles JavaScript control structures. It natively supports @else if
and @else, eliminating the need for ng-template workarounds.
Basic Syntax
<!-- Simple condition -->
@if (isLoggedIn()) {
<app-dashboard />
}
<!-- If with else -->
@if (user()) {
<h2>Welcome, {{ user().name }}</h2>
} @else {
<app-login-form />
}
<!-- If / else if / else -->
@if (status() === 'loading') {
<div class="spinner">Loading...</div>
} @else if (status() === 'error') {
<div class="error">An error occurred</div>
} @else if (status() === 'empty') {
<div class="empty">No results found</div>
} @else {
<app-data-table [data]="data()" />
}
Alias with the "as" Keyword
A powerful feature of @if is the ability to assign an alias to
the result of the condition. This is particularly useful when working with Signals
or Observables that return nullable values.
<!-- Save the condition result in a local variable -->
@if (currentUser(); as user) {
<div class="profile">
<h3>{{ user.name }}</h3>
<p>Email: {{ user.email }}</p>
<p>Role: {{ user.role }}</p>
</div>
}
<!-- Useful with async pipes or transformations -->
@if (users() | filterActive; as activeUsers) {
<p>Active users: {{ activeUsers.length }}</p>
}
Key Differences from *ngIf
@ifrequires no imports (it is not a directive, it is part of the compiler)@else ifis natively supported, eliminating the need for nestedng-templateblocks- The
assyntax works as aliasing of the truthy value of the condition - Type narrowing works correctly: inside the
@ifblock the type is already narrowed - No more
CommonModuleor direct import ofNgIfis needed
@for with track: Performant and Mandatory Iteration
The @for block replaces *ngFor with a fundamental
change: the track expression is mandatory. This design choice forces
developers to define how Angular uniquely identifies each item in the list, preventing
performance issues that went unnoticed with *ngFor.
Basic Syntax
<!-- Basic iteration with track by id -->
@for (product of products(); track product.id) {
<div class="product-card">
<h3>{{ product.name }}</h3>
<p>{{ product.price | currency:'EUR' }}</p>
</div>
}
<!-- With implicit variables -->
@for (item of items(); track item.id; let i = $index; let isFirst = $first; let isLast = $last) {
<div class="item" [class.first]="isFirst" [class.last]="isLast">
<span class="index">{{ i + 1 }}.</span>
<span>{{ item.name }}</span>
</div>
}
<!-- Track by index (for lists without unique id) -->
@for (label of labels(); track $index) {
<span class="tag">{{ label }}</span>
}
Implicit Variables Available in @for
| Variable | Type | Description |
|---|---|---|
$index | number | Current index (0-based) |
$first | boolean | true if this is the first element |
$last | boolean | true if this is the last element |
$even | boolean | true if the index is even |
$odd | boolean | true if the index is odd |
$count | number | Total number of elements |
Why track is Mandatory
When Angular updates a list, it needs to decide which DOM elements to recreate, which
to move, and which to keep unchanged. The track expression gives Angular
a stable identity for each element, enabling optimal updates.
// Component with a frequently updated list
@Component({
selector: 'app-live-feed',
standalone: true,
template: `
<!-- CORRECT: track by unique identity -->
<!-- When the list changes, Angular reuses existing DOM nodes -->
@for (post of posts(); track post.id) {
<app-post-card [post]="post" />
}
<!-- DISCOURAGED: track by index -->
<!-- If an element is added at the top, ALL nodes are recreated -->
@for (post of posts(); track $index) {
<app-post-card [post]="post" />
}
`
})
export class LiveFeedComponent {
posts = signal<Post[]>([]);
addNewPost(post: Post) {
// With track post.id: only 1 DOM node created
// With track $index: N DOM nodes recreated
this.posts.update(list => [post, ...list]);
}
}
When to Use $index as track
Use track $index only when the list items do not have a stable identity
(for example, an array of simple strings). In all other cases, always prefer a unique
property like id. Using $index on frequently changing lists
is one of the main causes of poor performance in Angular applications.
@switch: Clean Multiple Selection
The @switch block replaces the combination of ngSwitch,
*ngSwitchCase, and *ngSwitchDefault directives with a much
more natural and readable syntax.
<!-- BEFORE: ngSwitch (verbose, requires container) -->
<div [ngSwitch]="orderStatus">
<div *ngSwitchCase="'pending'">
<span class="badge warning">Pending</span>
</div>
<div *ngSwitchCase="'processing'">
<span class="badge info">Processing</span>
</div>
<div *ngSwitchCase="'shipped'">
<span class="badge success">Shipped</span>
</div>
<div *ngSwitchCase="'delivered'">
<span class="badge success">Delivered</span>
</div>
<div *ngSwitchDefault>
<span class="badge">Unknown</span>
</div>
</div>
<!-- AFTER: @switch (clean, no container) -->
@switch (orderStatus()) {
@case ('pending') {
<span class="badge warning">Pending</span>
}
@case ('processing') {
<span class="badge info">Processing</span>
}
@case ('shipped') {
<span class="badge success">Shipped</span>
}
@case ('delivered') {
<span class="badge success">Delivered</span>
}
@default {
<span class="badge">Unknown</span>
}
}
Notes on @switch
@switchuses strict comparison (===), identical to JavaScript- It does not require a container element: the comparison is directly on the expression
@defaultis optional but recommended to handle unexpected cases- It does not support fall-through: each
@caseis isolated (a desirable behavior) - Unlike
ngSwitch, no imports fromCommonModuleare needed
@defer: Declarative Lazy Loading
@defer is the true revolution introduced with the new template syntax.
It allows loading components, directives, and pipes on-demand directly
from the template, without any TypeScript code for lazy loading. The Angular compiler
automatically separates the code referenced inside @defer into separate
chunks, downloaded only when the specified trigger activates.
Basic Syntax and Associated Blocks
@defer (on viewport) {
<!-- Lazily loaded content -->
<app-heavy-chart [data]="chartData()" />
} @placeholder {
<!-- Shown before loading begins -->
<div class="chart-placeholder">
<p>The chart will appear here</p>
</div>
} @loading (after 150ms; minimum 300ms) {
<!-- Shown while the chunk is downloading -->
<div class="spinner">Loading chart...</div>
} @error {
<!-- Shown if loading fails -->
<div class="error">Unable to load the chart</div>
}
Blocks Associated with @defer
| Block | Purpose | Options |
|---|---|---|
@placeholder |
Content shown before the trigger activates | minimum: minimum display time |
@loading |
Content shown while the chunk is downloading | after: delay before showing; minimum: minimum duration |
@error |
Content shown if loading fails | No options |
Available Triggers
@defer supports several triggers that determine when to begin
loading the lazy content. They can be combined to create sophisticated loading conditions.
<!-- 1. on viewport: loads when the element enters the viewport -->
@defer (on viewport) {
<app-comments [postId]="postId()" />
} @placeholder {
<div style="height: 200px">Scroll to see comments</div>
}
<!-- 2. on idle: loads when the browser is idle -->
@defer (on idle) {
<app-analytics-widget />
}
<!-- 3. on interaction: loads on click/focus on a specific element -->
<button #loadBtn>Show details</button>
@defer (on interaction(loadBtn)) {
<app-product-details [id]="productId()" />
} @placeholder {
<p>Click the button to see details</p>
}
<!-- 4. on hover: loads when the mouse hovers over an element -->
<div #previewArea>Hover for preview</div>
@defer (on hover(previewArea)) {
<app-image-preview [url]="imageUrl()" />
}
<!-- 5. on timer: loads after a time interval -->
@defer (on timer(5s)) {
<app-newsletter-popup />
}
<!-- 6. when: loads when a condition becomes true -->
@defer (when isAuthenticated()) {
<app-admin-panel />
} @placeholder {
<p>Log in to access the admin panel</p>
}
<!-- 7. Combining triggers -->
@defer (on viewport; on timer(10s)) {
<app-sidebar-ads />
} @placeholder {
<div class="ad-placeholder"></div>
}
@defer Trigger Summary
| Trigger | Activation | Typical use case |
|---|---|---|
on viewport |
The placeholder element enters the viewport | Below-the-fold content, comments, footer |
on idle |
The browser is idle (requestIdleCallback) | Non-critical widgets, analytics |
on interaction |
Click or focus on a specific element | Expandable panels, on-demand details |
on hover |
Mouse hover on a specific element | Complex tooltips, previews |
on timer(Ns) |
After N seconds/milliseconds | Popups, delayed content |
when condition |
When the expression becomes truthy | State-based content (login, feature flags) |
Intelligent Prefetching
@defer also supports prefetching: you can start
downloading the chunk before the rendering trigger activates. This improves
the user experience because the content is already ready when needed.
<!-- Prefetch on idle, render on viewport -->
<!-- The browser downloads the chunk when idle, -->
<!-- but renders it only when it enters the viewport -->
@defer (on viewport; prefetch on idle) {
<app-heavy-data-grid [data]="gridData()" />
} @placeholder {
<div class="grid-skeleton">
<div class="skeleton-row"></div>
<div class="skeleton-row"></div>
<div class="skeleton-row"></div>
</div>
}
<!-- Prefetch on hover, render on interaction -->
<!-- The user hovers and the chunk downloads, -->
<!-- when they click the content appears instantly -->
<button #editBtn>Edit Profile</button>
@defer (on interaction(editBtn); prefetch on hover(editBtn)) {
<app-profile-editor [user]="currentUser()" />
} @placeholder {
<p>Click to edit your profile</p>
}
@empty: Handling Empty Lists Gracefully
The @empty block is a companion to @for that allows
showing alternative content when the iterated collection is empty. Before the new syntax,
this required a separate *ngIf to check the array length.
<!-- BEFORE: two separate directives -->
<div *ngIf="notifications.length === 0" class="empty-state">
<p>No notifications</p>
</div>
<div *ngFor="let notification of notifications">
{{ notification.message }}
</div>
<!-- AFTER: integrated into the control flow -->
@for (notification of notifications(); track notification.id) {
<div class="notification" [class]="notification.type">
<span class="icon">{{ notification.icon }}</span>
<p>{{ notification.message }}</p>
<time>{{ notification.date | date:'short' }}</time>
</div>
} @empty {
<div class="empty-state">
<img src="/assets/no-notifications.svg" alt="No notifications" />
<h3>All caught up!</h3>
<p>You have no notifications at the moment.</p>
</div>
}
Benefits of @empty
- Eliminates the duplication of the
array.length === 0check - The @empty block is rendered only when the collection has zero elements
- Improves readability by grouping iteration logic and empty state together
- Works with any iterable: arrays, Signal arrays, filtered results
Automatic Migration with Schematics
The Angular team provides an official schematic that automatically migrates templates from the old syntax to the new one. This tool analyzes every template, converts the structural directives, and generates clean code.
# Automatic control flow migration
ng generate @angular/core:control-flow-migration
# The command:
# 1. Analyzes all application templates
# 2. Converts *ngIf -> @if
# 3. Converts *ngFor -> @for (with track $index as default)
# 4. Converts ngSwitch -> @switch
# 5. Removes CommonModule imports where no longer needed
# For a single component
ng generate @angular/core:control-flow-migration --path=src/app/components/my-component
# For a dry run (preview without modifying files)
ng generate @angular/core:control-flow-migration --dry-run
Automatic Migration Caveats
- Default track: The schematic inserts
track $indexas the default for all@forblocks. After migration, review each occurrence and replace with the correct unique property (e.g.,track item.id) - Formatting: The migration may alter indentation. Run your project formatter after the migration
- Complex templates: Templates with deeply nested structural directives may require manual review
- Testing: Run all tests after migration to verify that behavior is unchanged
- Commit first: Commit your code before running the migration so you can easily compare the differences
<!-- BEFORE migration -->
<div *ngIf="users$ | async as users; else loading">
<table>
<tr *ngFor="let user of users; trackBy: trackByUserId; let i = index">
<td>{{ i + 1 }}</td>
<td>{{ user.name }}</td>
<td [ngSwitch]="user.status">
<span *ngSwitchCase="'active'" class="green">Active</span>
<span *ngSwitchCase="'inactive'" class="red">Inactive</span>
<span *ngSwitchDefault class="gray">N/A</span>
</td>
</tr>
</table>
</div>
<ng-template #loading>
<div class="spinner">Loading...</div>
</ng-template>
<!-- AFTER automatic migration -->
@if (users$ | async; as users) {
<table>
@for (user of users; track user.id; let i = $index) {
<tr>
<td>{{ i + 1 }}</td>
<td>{{ user.name }}</td>
<td>
@switch (user.status) {
@case ('active') {
<span class="green">Active</span>
}
@case ('inactive') {
<span class="red">Inactive</span>
}
@default {
<span class="gray">N/A</span>
}
}
</td>
</tr>
}
</table>
} @else {
<div class="spinner">Loading...</div>
}
Advanced Patterns: Combining Control Flow
The true power of the new control flow emerges when you combine the various constructs to solve real-world scenarios. Let us explore some advanced patterns commonly used in production applications.
Pattern 1: Dashboard with Lazy Loading per Section
<div class="dashboard">
<!-- Header always visible -->
<header>
<h1>Dashboard</h1>
@if (user(); as u) {
<span>Welcome, {{ u.name }}</span>
}
</header>
<!-- KPIs loaded immediately when idle -->
@defer (on idle) {
<app-kpi-cards [metrics]="kpiData()" />
} @placeholder {
<div class="kpi-skeleton">
@for (i of [1,2,3,4]; track $index) {
<div class="skeleton-card"></div>
}
</div>
}
<!-- Chart loaded when it enters the viewport -->
@defer (on viewport; prefetch on idle) {
<app-revenue-chart [data]="revenueData()" />
} @placeholder {
<div class="chart-placeholder" style="height: 400px">
<p>Revenue chart</p>
</div>
} @loading (after 200ms) {
<div class="spinner">Loading chart...</div>
}
<!-- Data table loaded on demand -->
<button #showTable>Show details</button>
@defer (on interaction(showTable); prefetch on hover(showTable)) {
@if (tableData(); as data) {
@for (row of data; track row.id) {
<app-data-row [row]="row" />
} @empty {
<p>No data available for the selected period</p>
}
}
} @placeholder {
<p>Click to view details</p>
}
</div>
Pattern 2: List with Filters, Sorting, and Empty State
<div class="product-list">
<!-- Filters -->
<div class="filters">
@for (category of categories(); track category.id) {
<button
[class.active]="selectedCategory() === category.id"
(click)="selectCategory(category.id)">
{{ category.name }}
<span class="count">({{ category.count }})</span>
</button>
}
</div>
<!-- Loading state -->
@if (isLoading()) {
<div class="loading">
@for (i of [1,2,3]; track $index) {
<div class="product-skeleton"></div>
}
</div>
} @else {
<!-- Product list -->
@for (product of filteredProducts(); track product.id) {
<div class="product-card">
<h3>{{ product.name }}</h3>
<p>{{ product.description }}</p>
@switch (product.availability) {
@case ('in_stock') {
<span class="badge success">In Stock</span>
}
@case ('low_stock') {
<span class="badge warning">Low Stock ({{ product.stock }})</span>
}
@case ('out_of_stock') {
<span class="badge danger">Out of Stock</span>
}
}
<div class="price">{{ product.price | currency:'EUR' }}</div>
</div>
} @empty {
<div class="empty-state">
<h3>No products found</h3>
<p>Try adjusting your search filters</p>
<button (click)="resetFilters()">Reset filters</button>
</div>
}
}
</div>
Pattern 3: Conditional @defer with User Permissions
@Component({
selector: 'app-settings-page',
standalone: true,
template: `
<h1>Settings</h1>
<!-- Profile section: always available -->
<app-profile-settings />
<!-- Admin panel: loaded only if the user is admin -->
@defer (when isAdmin()) {
<app-admin-settings />
} @placeholder {
@if (!isAdmin()) {
<div class="restricted">
<p>Access restricted to administrators</p>
</div>
}
}
<!-- Advanced settings: defer + condition -->
@if (showAdvanced()) {
@defer (on idle) {
<app-advanced-settings />
} @loading {
<div class="spinner">Loading advanced settings...</div>
}
}
`
})
export class SettingsPageComponent {
private authService = inject(AuthService);
isAdmin = computed(() =>
this.authService.currentUser()?.role === 'admin'
);
showAdvanced = signal(false);
}
Performance Impact
The new control flow is not just more readable: it has concrete, measurable advantages for application performance. The Angular compiler generates optimized code that eliminates the runtime overhead of structural directives.
Benchmarks: New Control Flow vs Structural Directives
| Metric | Directives (*ngIf, *ngFor) | New Control Flow | Improvement |
|---|---|---|---|
| Bundle size (CommonModule) | ~12KB (minified) | 0KB (built-in compiler) | -100% |
| Rendering 1000-element list | ~45ms | ~38ms | -15% |
| Re-rendering with single change | ~8ms | ~3ms | -62% |
| Memory footprint per @for | Directive + template ref + embedded view | Compiled instructions only | Significant |
| Lazy loading with @defer | Not available (route-level only) | Component/template-level | New capability |
Why the New Control Flow is Faster
- Direct compilation:
@ifand@forblocks are compiled into optimized JavaScript instructions by the Angular compiler, without the overhead of creating directive instances at runtime - No ViewContainerRef: Structural directives use
ViewContainerRefto create/destroy embedded views. The new control flow manages the DOM directly - Optimized track: The
@fordiffing algorithm has been rewritten from scratch to be more efficient thanDefaultIterableDiffer - Tree shaking: Since they are not directives, they occupy no bundle space if unused. Even when used, the generated code is minimal
- @defer chunk splitting: The compiler automatically creates separate chunks for lazy content, reducing the initial payload
Best Practices for the New Control Flow
After months of production usage by the Angular community, clear best practices have been established to get the most out of the new control flow.
10 Best Practices for the New Control Flow
-
Always use a unique property in track: Avoid
track $indexunless the list contains primitive values without identity. Prefertrack item.id -
Use @empty for empty states: Every
@forthat displays data to the user should have an@emptyblock with a helpful message -
Use @defer for below-the-fold content: Everything not visible at
the first render should be wrapped in
@defer (on viewport) -
Combine prefetch and trigger: Use
prefetch on idleto pre-download chunks during browser idle moments -
Prefer @else if over @switch for few cases: If you have 2-3
conditions,
@if / @else ifis more readable than@switch - Always provide a @placeholder for @defer: The user must understand that content is about to appear; an empty placeholder is confusing
-
Use minimum and after in @loading: Avoid the loading "flash"
for quickly-loading content with
@loading (after 150ms; minimum 300ms) - Do not nest too deeply: More than 3 levels of nested control flow makes the template unreadable. Extract sub-components instead
- Migrate completely: Do not mix old and new syntax in the same template; it confuses developers and complicates maintenance
-
Review after automatic migration: The schematic uses
track $indexas default. Review every@forand assign the correct track expression
Anti-patterns to Avoid
<!-- MISTAKE 1: Track by $index on dynamic lists -->
<!-- If an element is inserted at the top, ALL DOM nodes are recreated -->
@for (user of users(); track $index) {
<app-user-card [user]="user" /> <!-- Unnecessarily recreated! -->
}
<!-- FIX: track user.id -->
<!-- MISTAKE 2: @defer without placeholder -->
<!-- The user sees empty space without understanding what is happening -->
@defer (on viewport) {
<app-comments />
}
<!-- FIX: add @placeholder with visual indication -->
<!-- MISTAKE 3: @defer for critical above-the-fold content -->
<!-- Main content must not be lazy loaded -->
@defer (on idle) {
<app-hero-section /> <!-- NEVER! The hero must be immediate -->
}
<!-- MISTAKE 4: Excessive nesting -->
@if (condition1()) {
@for (item of items(); track item.id) {
@if (item.visible) {
@switch (item.type) {
@case ('A') {
@for (sub of item.children; track sub.id) {
<!-- Too deeply nested! Extract a component -->
}
}
}
}
}
}
<!-- FIX: extract sub-components to reduce nesting -->
Migration Checklist
Control Flow Migration Checklist
- Commit your current code before migration
- Run
ng generate @angular/core:control-flow-migration --dry-run - Review the proposed changes
- Run the actual migration
- Replace all
track $indexoccurrences with unique properties - Add
@emptyblocks where appropriate - Evaluate where to add
@deferfor lazy loading - Remove imports of
CommonModule,NgIf,NgFor,NgSwitch - Run the formatter to normalize indentation
- Run all tests (unit + e2e)
- Visually verify every page
Summary and Next Steps
Angular's new control flow represents a qualitative leap in the framework's developer
experience. The syntax with @if, @for, @switch,
and @defer is not just more readable: it produces more performant code,
enables declarative lazy loading, and enforces best practices like mandatory track expressions.
Key Concepts from This Article
- @if / @else if / @else: Replaces
*ngIfwith native support for else-if and aliasing viaas - @for with track: Replaces
*ngForwith mandatory track for guaranteed performance. Includes implicit variables ($index, $first, $last, $even, $odd, $count) - @switch / @case / @default: Replaces
ngSwitchwith clean syntax and strict comparison - @defer: Declarative lazy loading with triggers (viewport, idle, interaction, hover, timer, when) and prefetching
- @empty: Graceful handling of empty lists, integrated directly into
@for - Migration: The
@angular/core:control-flow-migrationschematic automates the conversion - Performance: Zero bundle overhead (compiled, not directives), optimized diffing, chunk splitting with
@defer
In the next article of the series we will explore Standalone Components, the architecture that completely eliminates NgModules from Angular, simplifying the application structure and improving tree-shaking and lazy loading at the route level.
Related deep dive: 1 - Angular Signals: da Zero a Produzione.
Related deep dive: 2 - Zoneless Change Detection: addio Zone.js.
Frequently asked questions
What is the main reason for introducing a new template control flow system in Angular 17?
The new syntax replaces structural directives like ngIf, ngFor, and ngSwitch with more readable, performant, and feature-rich constructs.
How does the @if directive differ from its predecessor ngIf in terms of readability?
The @if directive natively supports if-else statements, eliminating the need for workarounds and making the code more similar to JavaScript control structures.
What is the purpose of the track expression in the @for directive?
The track expression gives Angular a stable identity for each element in the list, enabling optimal updates when the list changes.
How does the @defer directive improve application performance and user experience?
The @defer directive allows loading components, directives, and pipes on-demand directly from the template, without any TypeScript code for lazy loading, which improves performance and user experience by prefetching content before rendering.
Related deep dive: Zero Articoli Prerenderizzati: Perché il Prerender Totale è un Rischio di Build, non un'Ottimizzazione.
Commenti
Caricamento commenti...
Sign in to comment