Display Angular @empty block at the right time!
Learn to display an @empty Angular block at the right time!
The new Control Flow syntax for Angular has been introduced with Angular 17 release, as a developer preview.
It replaces *ngIf
, *ngFor
, and *ngSwitch
with a new syntax that is more expressive and easier to read.
Old syntax with *ngFor
<article *ngFor="let article of articles$ | async; trackBy articleTrackBy">
<h2>{{ article.title }}</h2>
<p>{{ article.content }}</p>
</article>
New syntax with @for
@for(article of articles$ | async; track article.id) {
<article>
<h2>{{ article.title }}</h2>
<p>{{ article.content }}</p>
</article>
}
The Angular team added a new feature to enhance the @for block: the @empty block.
The content of this block will be displayed if the list is empty or undefined.
@for(article of articles$ | async; track article.id) {
<article>
<h2>{{ article.title }}</h2>
<p>{{ article.content }}</p>
</article>
} @empty {
<p>No articles found</p>
}
The problem
A common misconception is to think that the @empty block is evaluated only once the data is retrieved from an asynchronous call.
In most usecases, you iterate over a list of items retrieved from an API.
As the API call is asynchronous, the list is undefined/empty at the component initialization.
From our point of view as an Angular developer:
- The list is evaluated as empty
- The data is retrieved from the API
- The list is evaluated once again and the empty block is displayed if the list is empty
From an end user point of view:
- The empty block is displayed at the beginning
- The view is updated with the list of items once the data is retrieved if a non-empty list is returned
It means we are displaying the empty block at the wrong time!
For a second (or more depending on your API response time), the user see the empty block before the list is displayed and
might mistakenly believe that the list is really empty, while it’s not.
The solution
You need to wrap your existing logic to deal with the loading state of the list.
@if(articles$ | async as articles) {
@for(article of articles; track article.id) {
<article>
<h2>{{ article.title }}</h2>
<p>{{ article.content }}</p>
</article>
} @empty {
<p>No articles found</p>
}
} @else {
<p>Loading...</p>
}
There, instead of displaying the @empty Angular block while you are waiting for the list to be retrieved, you display a loading message. The @empty block will be evaluated once the list is retrieved and displayed at the right time.