New Angular 17 feature: new control flow syntax
Tags: angular, typescript, javascript
Published: 2023-10-31 08:21:16

---
title: New Angular 17 feature: new control flow syntax
published: true
description: Angular's new declarative control flow demonstrated by Signal-based examples
tags: angular, typescript, javascript
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a8xcvgc893ceiq3k2anf.png
# Use a ratio of 100:42 for best results.
# published_at: 2023-10-23 20:47 +0000
canonical_url: https://www.angularaddicts.com/p/angular-17-new-control-flow-with-signals
---


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](https://www.angularaddicts.com/p/angular-17-feature-deferred-loading-with-signals)
* blocks for providing **conditional rendering** and **rendering the items of a collection** ([RFC](https://github.com/angular/angular/discussions/50719)): 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

The full source code is available here:

[https://github.com/gergelyszerovay/angular-17-control-flow](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](https://github.com/gergelyszerovay/angular-17-control-flow/blob/main/src/app/app.component.html) template file:


```html
<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 `&lt;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 "&#64;" HTML entity instead. [plugin angular-compiler]
* [ERROR] NG5002: Unexpected character "EOF" (Do you have an unescaped "{" in your template? Use `"{{ '{' }}"`) to escape it.)


## Using `@for` blocks to render items of a collection

Let's define the `items` array in the component's class: 
 
```ts
  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:


```html
<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:


```html
<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`:


```html
<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](https://www.angularaddicts.com/p/angular-17-feature-deferred-loading-with-signals).

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


## 👨‍💻About the author

My name is [Gergely Szerovay](https://www.linkedin.com/in/gergelyszerovay/), 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](https://angularaddicts.com/). 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](https://www.angularaddicts.com/) 🔥

Follow me on [Substack](https://www.angularaddicts.com/), [Medium](https://medium.com/@GergelySzerovay), [Dev.to](https://dev.to/gergelyszerovay), [Twitter](https://twitter.com/GergelySzerovay) or [LinkedIn](https://www.linkedin.com/in/gergelyszerovay/) to learn more about Angular!