How does Angular's New Control Flow work?
The most significant change they have ever made to Angular templates

Front-end software engineer and I specialise in Angular🤖. I've been working with it since Angular JS and I like to keep up with new technologies. I love to write about Angular, TypeScript, JavaScript and more 📰. My hobbies are travelling around Southeast Asia ✈️, cars and technology.
This is one of the biggest features of Angular 17, the new declarative control flow incorporates NgIf, NgFor, and NgSwitch into the framework, while deferrable views allow templates to load dependencies lazily, triggered by one or more configurable conditions.
In June of 2023, the Angular team proposed two RFC, for control flow and deferrable views to have the feedback community. Once the community discussed the new features, the Angular team made a few changes to the original idea, and now, a few months later here we are.
We no longer use Zone.js with our new syntax. Angular is gradually becoming less dependent on this library, which is a significant step. It's important to note that Zone.js will still be used for the old syntax and other features.
@ if @ else
If we compare the old and the new way of using @if, we reduce our code and we don't need to create the id for the else case.
<div *ngIf="userLogged; else noAuthorized">
Hi {{ userLogged.name }}
</div>
<ng-template #noAuthorized>
You need to log in first
</ng-template>
@if (userLogged) {
Hi {{ userLogged.name }}
} @else {
You need to log in first
}
@ for
We reduce our code again, but we have to use track in our @for, which is not a bad thing as it improves the performance of our application and we don't have to create the trackByFn function as before.
<div *ngFor="let user of users">
<option value="{{user.id}}">{{user.name}}</option>
</div>
@for( user of users; track user.id){
<option value="{{user.id}}">{{user.name}}</option>
}
@ switch
We are reducing our code and making it more readable.
<section [ngSwitch]="user.role">
<span *ngSwitchCase="'admin'">
You have admin role
</span>
<span *ngSwitchCase="'user'">
You have user role
</span>
<span *ngSwitchCase="'super'">
You have super role
</span>
<span *ngSwitchDefault>
You don't have a role yet
</span>
</section>
@switch (user.role) {
@case (admin) {
<span>You have admin role</span>
}
@case (user) {
<span>You have user role</span>
}
@case (super) {
<span>You have super role</span>
}
@default {
<span>You don't have a role yet</span>
}
}
Deferrable Views
This term refers to the fact that views can be rendered when a logical expression is triggered, which we can define as on hover, on interaction, on idle, on timer or on viewport.
There are a few states that we can use to control our view:
@ defer: Create a defer block.
@ placeholder: (Optional) Initially rendered content before loading.
@ error: (Optional) Shows when loading fails.
@ loading: (Optional) Shows content while loading.
on idle: Triggers on browser's idle time(idle - no user browser interaction).
on interaction: Triggers when user interacts with a particular element(on click, focus, touch, input events).
on immediate: Immediately loads/ pre-fetches the content.
on hover: Triggers on hover event.
on timer: Delays loading content for a specified time.
on viewport: Loads content when the element gets on the viewport.
prefetch: Prefetches the content when a contention is met (using "when" or on "keywords").
minium: Shows
@loadingor@placeholderblocks for minimum X ms.after: Shows
@loadingor@placeholderblocks after Y ms.
Take a look at a few examples, we have a boolean signal that triggers our content.
isCheckedDefer = signal(false);
We can also add a timer to delay the loading of the content
<input #checkboxDefer type="checkbox" [checked]="isCheckedDefer()" (change)="isCheckedDefer.set(checkboxDefer.checked)" id="checkboxDefer"/>
<label for="checkboxDefer">This checkbox load the <strong>app-about</strong> component</label>
@defer (when isCheckedDefer()) {
<app-about/>
}
@placeholder {
<span>Placeholder</span>
}
@error {
<span>Error</span>
}
@loading(minimum 1s) {
<span>Loading...</span>
}
In this example we can have 2 components, app-root and app-about, the app-about component is imported in app-root but not downloaded after we need it, when we click the checkbox the component downloads lazy and the placeholder view changes to the loading view, and then when the about component is ready it's finally rendered, if any error happens during the process the @error block will display.
Using @ defer with hover, idle, timer and viewport
As we know, deferred blocks support this declarative trigger type: on interaction, on hover, on idle, on timer, on viewport.
@ defer on interaction
When the user interacts with the @placeholder block, the on interaction, block is rendered. An interaction can refer to a click, touch focus, or input events, etc.
@defer (on interaction) {
<span>Clicked</span>
}
@placeholder {
<span>Placeholder (click on it!)</span>
}
@ defer on hover
When the user hovers over the @placeholder block, Angular renders the on-hover block.
@defer (on hover) {
<span>Hovered</span>
}
@placeholder {
<span>Placeholder (hover it!)</span>
}
@ defer on idle
Angular shows the on idle section when the browser goes idle after loading the page.
@defer (on idle) {
<span>Browser has reached an idle state</span>
}
@placeholder {
<span>Placeholder</span>
}
@ defer on timer
The block for the timer is displayed when the specified duration has elapsed.
@defer (on timer(3s)) {
<span>Visible after 3s</span>
}
@placeholder {
<span>Placeholder</span>
}
@ defer on viewport
When the placeholder comes into view in the browser, Angular displays the on viewport block. This is a good way of deferring components that you are not sure will need until they scroll down the page.
@defer (on viewport) {
<span>The block entered the viewport<span/>
}
@placeholder {
<span>Placeholder</span>
}
We can open our browser's developer tools and see that the components in @defer are only downloaded when triggered, not only the components but also the directives and pipes are loaded lazily.
Conclusion
In this article, we took a look at the first impressions of Angular's new control flow. These are just a few demos, but I know that this feature has great potential for optimizing our applications, now we need some time to integrate it and see how much it can improve our applications.
Related articles:






