Angular Addicts

Share this post

New Angular 17 feature: new control flow syntax

www.angularaddicts.com

Discover more from Angular Addicts

In this publication we collect hand-selected articles & other resources —  e.g. books, talks, podcast episodes — about Angular. Whether you are a professional or a beginner, you’ll find valuable Angular knowledge here.
Over 1,000 subscribers
Continue reading
Sign in

New Angular 17 feature: new control flow syntax

Angular's new declarative control flow demonstrated by Signal-based examples

Gergely Szerovay
Oct 31, 2023
9
Share this post

New Angular 17 feature: new control flow syntax

www.angularaddicts.com
2

Angular 17 is going to be released in the beginning of November, and it is going to offer a new template control block syntax with a declarative control flow. Two groups of features use these new blocks:

  • the deferred loading blocks: Angular 17 is going to have a @defer control block enabling lazy-loading of the block's content. Lazy-loading also applies to the dependencies of the content of the block: all the components, directives and pipes are going to be lazy-loaded, too. I demonstrated by examples how defer blocks work in my previous article

  • blocks for providing conditional rendering and rendering the items of a collection (RFC): these are alternatives for the NgIf, NgFor, and NgSwitch directives

One of the most important benefits of these new control blocks is that they will support zoneless applications with Signals.

In this article, I demonstrate how to:

  • use the new control block syntax and create conditionally rendered blocks with @if and @else

  • create switch and case blocks with @switch, @case and @default

  • create loops with @for, and handle empty collections with the @empty block

  • migrate ngIf, ngFor, and ngSwitch to the new control block syntax

This article is also available on dev.to with better source code syntax highlighting.

The full source code is available here:

https://github.com/gergelyszerovay/angular-17-control-flow

I used Angular v17.0.0-next.8 with standalone components and Signals. You can start the frontend by yarn run start or npm run start.

Conditionally rendered control blocks: @if and @else

In the first example, I create a checkbox and bind it to the isChecked signal. The signal's default value is true, so initially the checkbox is checked, and the content of the @if block is rendered. The examples below are from the src\app\app.component.html template file:

<h3>&#64;if and &#64;else</h3>
<div>
  <input #checkbox type="checkbox" [checked]="isChecked()"
    (change)="isChecked.set(checkbox.checked)" id="checkbox"/>
</div>
<div>
@if (isChecked()) {
  <span>Checked</span>
} 
@else {
  <span>Not checked</span>
}
</div>

The @if (logical_expression) { statement creates a @if block with a logical expression. I use the isChecked() signal as a logical expression, as it evaluates to a boolean value.

I added an @else block under the @if block, it's rendered when the logical expression in the @if block evaluates to false, so in our case if the value of the isChecked() signal is false. So if I uncheck the checkbox, Angular renders the contents of the @else block.

There is one more important thing regarding the new control block syntax: since the @, { and } characters have a special meaning, we have to replace them in the text of the templates using their HTML entities. We need to use:

  • &#64; instead of @, see the <h3> heading in my code above

  • &#123; instead of {

  • &#125; instead of }

Otherwise we get one of the following compile-time errors:

  • [ERROR] NG5002: Incomplete block "...". If you meant to write the @ character, you should use the "@" HTML entity instead. [plugin angular-compiler]

  • [ERROR] NG5002: Unexpected character "EOF" (Do you have an unescaped "{" in your template? Use "{{ '{' }}") to escape it.)


Thanks for reading Angular Addicts! Subscribe for free to receive new posts and support my work.

Using @for blocks to render items of a collection

Let's define the items array in the component's class:

collection = [
    { id: 1, name: 'Item 1' },
    { id: 2, name: 'Item 2' },
    { id: 3, name: 'Item 3' }
  ];

We can use an @for (item of items; track item.id) { block to render a collection's elements:

<ul>
@for (item of collection; track item.id; let index = $index, first = $first; let last = $last, even = $even, odd = $odd; let count = $count) {

<li><strong>{{item.name}}</strong> index={{index}} first={{first}} last={{last}} even={{even}} odd={{odd}} count={{count}}</li>

}
</ul>

Each item in the collection needs to have a unique property (like the id), and we need to refer to this value with the track argument. If the collection contains strings or numbers, not objects, we can use the item itself as a track value: @for (item of items; track item) {.

Next to the current item, the @for expression enables us to access the following values inside the block:

  • $index: the index of the item in the collection

  • $even: true, if the index is even

  • $odd: true, if the index is odd

  • $count: the number of the items in the collection

  • $first: true, if the current item is the first in the collection

  • $last: true, if the current item is the last in the collection

Using an @empty block to handle the empty collections passed to @for

We can add an @empty block under the @for block. The @empty block's content is rendered if the collection we passed to the @for block is empty:

<ul>
@for (item of emptyCollection; track item.id;) {
<li><strong>{{item.name}}</strong></li>
}
@empty {
  <span>The collection is empty</span>
}
</ul>

Switch control flow with with @switch, @case and @default

In my next example, I create four radio buttons and a radioValue signal. The signal's initial value is 1, and when the user clicks on the radio buttons, we change the signaal's value to 1, 2, 3 or 4:

<div>
  <div>
    <input type="radio" [checked]="radioValue() === 1" (change)="radioValue.set(1)" id="radio1"/>
    <label for="radio1">1</label>
  </div>
  <div>
    <input type="radio" [checked]="radioValue() === 2" (change)="radioValue.set(2)" id="radio2"/>
    <label for="radio2">2</label>
  </div>
  <div>
    <input type="radio" [checked]="radioValue() === 3" (change)="radioValue.set(3)" id="radio3"/>
    <label for="radio3">3</label>
  </div>
  <div>
    <input type="radio" [checked]="radioValue() === 4" (change)="radioValue.set(4)" id="radio4"/>
    <label for="radio4">4</label>
  </div>
</div>
<div>
@switch (radioValue()) {
  @case (1) {
    <span>Case 1</span>
  }
  @case (2) {
    <span>Case 2</span>
  }
  @default {
    <span>Default case (Not 1 or 2)</span>
  }
}
</div>

I nested three blocks into the @switch block:

  • the contents of the @case (1) { block is rendered, when the radioValue() signal equals to 1

  • the contents of the @case (2) { block is rendered, when the radioValue() signal equals to 2

  • and the contents of the @default { block is rendered, when the radioValue() signal doesn't equal to the values we specified in the @case blocks

How to migrate ngIf, ngFor, and ngSwitch to the new control block syntax

To convert the old structural directives in an app's templates to the new control blocks, run the following schematic:

ng g @angular/core:control-flow-migration

Summary

In this article, I demonstrated how the new control flow in Angular 17 is going to work: I showed you how to create conditional blocks and loops using the new control block syntax. I hope you have found my tutorial useful!

In the first part of this article series, I explain how the new deferred blocks work and how to specify conditions to trigger the loading and rendering of these blocks' content: New Angular 17 feature: deferred loading.

And as always, please let me know if you have some feedback!

👨‍💻About the author

My name is Gergely Szerovay, I work as a frontend development chapter lead. Teaching (and learning) Angular is one of my passions. I consume content related to Angular on a daily basis — articles, podcasts, conference talks, you name it.

I created the Angular Addict Newsletter so that I can send you the best resources I come across each month. Whether you are a seasoned Angular Addict or a beginner, I got you covered.

Next to the newsletter, I also have a publication called — you guessed it — Angular Addicts. It is a collection of the resources I find most informative and interesting. Let me know if you would like to be included as a writer.

Let’s learn Angular together! Subscribe here 🔥

Follow me on Substack, Medium, Dev.to, Twitter or LinkedIn to learn more about Angular!


Thanks for reading Angular Addicts! Subscribe for free to receive new posts and support my work.

9
Share this post

New Angular 17 feature: new control flow syntax

www.angularaddicts.com
2
Share
2 Comments
Share this discussion

New Angular 17 feature: new control flow syntax

www.angularaddicts.com
Bob
Nov 1

Angular is getting more and more jsf. Great, so the switch was totally useful. #Ironie.

Expand full comment
Reply
Share
Richard Toth
Oct 31

Great article, as always! :)

Expand full comment
Reply
Share
Top
New
Community

No posts

Ready for more?

© 2023 Gergely Szerovay
Privacy ∙ Terms ∙ Collection notice
Start WritingGet the app
Substack is the home for great writing

Our use of cookies

We use necessary cookies to make our site work. We also set performance and functionality cookies that help us make improvements by measuring traffic on our site. For more detailed information about the cookies we use, please see our privacy policy. ✖