Angular 17's New Control Flow Syntax Explored

Angular 17 shipped with a fresh control flow syntax, and it represents a significant shift from the previous approach. Alongside other features we'll examine in future posts, this new syntax marks a clear departure from how control flow has traditionally been written in Angular templates.

Control flow governs the sequence in which statements execute within a script. Conditional constructs (if…else, switch) determine which statements run or are skipped based on conditions, while loops enable repeated execution.

Subscribed

Angular now offers a new control flow syntax, a substantial change from the former style (referred to here as the old syntax), which remains in use since the new syntax is still in developer preview.

Let's begin by contrasting the new syntax against the old one.

Conditional Rendering with If

Suppose we need to display a template section only when conditions are met. Under the old syntax, the approach was:

A deep dive into new control flow syntax for Angular (17) — figure 1

Old if….else conditional syntax in Angular

With the new syntax, the same logic is expressed as:

A deep dive into new control flow syntax for Angular (17) — figure 2

New Angular if…else conditional syntax

This can be further condensed to:

A deep dive into new control flow syntax for Angular (17) — figure 3

Iterating with For Loops

Consider for loops:

Old

Angular Old For Loop Syntax

For Loop before V17

New

A deep dive into new control flow syntax for Angular (17) — figure 5

New Angular For Loop Syntax

Notice that we supply a tracking expression, which produces a unique key to bind each array item to its corresponding DOM position for performance optimization. This is mandatory in the new syntax, whereas it was optional previously.

Switch Statements with NgSwitch

Here's the old way:

A deep dive into new control flow syntax for Angular (17) — figure 6

And this is how it looks with the new syntax:

A deep dive into new control flow syntax for Angular (17) — figure 7

Did you spot the difference? The new syntax is more readable (admittedly subjective, but likely agreeable) and feels familiar—it mirrors the syntax you'd encounter when writing TypeScript, JavaScript, or most other languages. For newcomers, deciphering the old syntax often required a few puzzled looks and squinted eyes.

A deep dive into new control flow syntax for Angular (17) — figure 8

Another major advantage is syntax highlighting and formatting. With the old syntax, highlighting was limited because structural directives lived within HTML attributes. Now that control flow is separate from HTML tags, highlighting works out of the box. Additionally, the prettier npm package (ensure you're on the latest version) supports formatting for the new Angular syntax, which is quite impressive.

Combined, these features make it easy to distinguish where each block begins and ends, and nested blocks within templates become straightforward to spot. This should substantially enhance code readability.

And there's no longer a need for extraneous ng-container and ng-template wrappers for conditional HTML sections. This yields cleaner code with less boilerplate.

A deep dive into new control flow syntax for Angular (17) — figure 9

If only it were always that straightforward

We've covered if and for, but what about switch? Here's an example.

Enhancements Over the Prior Syntax

Mandatory track Expression for Loops

The new syntax requires a track expression that provides a unique key, linking each array item to its DOM view position for better performance, particularly with large lists.

Omitting it yields the following error:

@for loop must have a "track" expression
Enter fullscreen mode Exit fullscreen mode

Additionally, Angular now employs an optimized algorithm for the for loop, minimizing DOM operations in response to collection changes, making it more efficient.

The @empty Keyword for Empty Lists

We now have an @empty keyword to handle scenarios where the list is empty, which is quite handy.

A deep dive into new control flow syntax for Angular (17) — figure 10

Clearer Else Clauses

As demonstrated earlier, the else clause in the old control flow syntax was not reader-friendly and demanded considerable boilerplate. With the new control flow, it's far more readable and intuitive, especially for developers new to Angular.

A deep dive into new control flow syntax for Angular (17) — figure 11

Integration with Async Pipe

Just as before, the async pipe remains available for subscribing to observables.

Within for loops, this is how it looks:

A deep dive into new control flow syntax for Angular (17) — figure 12

The pattern applies to if blocks as well:

A deep dive into new control flow syntax for Angular (17) — figure 13

How to Migrate

First, ensure your Angular project is on v17. Then, run the schematic below to migrate existing control flow syntax to the new style.

ng g @angular/core:control-flow-migration
Enter fullscreen mode Exit fullscreen mode

When prompted for a path, provide the path to your project, and that's it.

One crucial caveat: this control flow syntax, a significant advancement for Angular, is currently in developer preview. This means internal changes may occur without following semantic versioning, giving the Angular team flexibility to address issues before it reaches general availability with the same guarantees Angular provides for all features.

Coming Up: The New @defer Syntax

Angular is also introducing a @defer syntax for lazy loading components, directives, and pipes in templates until specific conditions are satisfied. I'll explore this in the next edition of Unstacked in the coming weeks, as this post is already quite lengthy for a newsletter.

Conclusion

This post examined the new control flow syntax for Angular and its advantages over the old syntax. We saw that it is more familiar and arguably more readable, requiring less cognitive effort to understand, even for seasoned Angular developers, let alone beginners. We also noted how the new syntax not only replaces but enhances the old one, introducing helpful additions like @empty and the mandatory track expression for better performance.

That's all for now—until next time, keep learning.