Angular 17: A Hands-On Journey into Deferred Loading
A journey of discovering the new features of Angular 17
Introduction
Being at the forefront of web development trends has always been a passion of mine. When I caught wind of the upcoming release of Angular 17 (last up-to-date version is 17.0.0-rc.1) and the thrilling enhancements it promised, I felt an irresistible urge to delve deeper. So, just like any sensible developer (and slightly curious explorer), I decided to create a little side project to see what all the fuss was about.
Throughout this article and more to come, I’ll take you through my journey of exploring Angular 17 and show you the important changes and new features that developers can benefit from. So, get ready to dive into the latest Angular version with me and stay tuned for the ongoing exploration in the upcoming articles.
You can access all the source code examples featured in this article here https://github.com/hasnaoui-saif/Angular17/tree/main/src/app/deferred-loading
Deferred loading
First, let us wrap our head about the concept of the Lazy loading first. It is a trick that helps web apps load things, like scripts, only when you need them. So, instead of cramming everything in at the start, lazy loading makes the app wait until you do something, like scrolling or clicking, before fetching non-essential stuff. This not only makes your app faster, since it doesn’t have to carry around all the unnecessary baggage from the start , but also saves your precious data and gives the server a breather.
In the older versions of Angular, we had some cool ways to do lazy loading, like using the Router or dynamic imports. But guess what? In Angular 17, the Angular wizards have taken it to the next level with a new thing called @defer Deferred Loading — RFC
Logical expressions
In the initial example, I created a button and bind it to the isClicked
signal. The signal's default value is false
, so initially the isClicked
is false, and the content of the @defer
block is not rendered.
<button (click)="isClicked.set(true)" >
Click on me to load <strong>app-child</strong> component
</button>
@defer (when isClicked()) {
<app-child/>
}
@placeholder {
<span class='placeholder'>Placeholder</span>
}
@error {
<span class='error'>Error</span>
}
@loading(minimum 1s) {
<span class='loading'>Loading...</span>
}
Let us break this down:
The @defer (when logical_expression) {}
code creates a special block with a condition. In our case, we’re using the isClicked()
signal as the condition.
Inside this @defer
block, there are three optional blocks:
@placeholder
block: Initially shown before the@defer
condition is met.- When the
@defer
condition is met, Angular loads content e.g. from the server, and you see the@loading
block. - If loading fails, the
@error
block is displayed.
The use of the @placeholder
, @loading
and @error
blocks are optional, so that means we can use standalone @defer
blocks too.
Let’s see how this code works! When we open the app, the isClicked()
signal is false
, so the @defer
block is not triggered and the content of the @placeholder
block is visible:
Now, when we click on the button, the signal isClicked()
turns true. As a result, Angular loads the content inside the @defer
block, replacing the content of the @placeholder
block with the content of the @loading
block. I’ve set a minimum duration condition for this block, ensuring it’s visible for at least one second. If the content inside the @defer
block loads within this waiting period, Angular won’t show the “@loading
” block.
Afterward, the content inside the @defer
block, which includes the <app-child>
component, is visible.
The interesting part is that we can observe the child component loading shortly after clicking the button. One might wonder how this happens ?
To find out, let’s open the Developer tools. There, we can see that immediately after the button click, Angular loads a new chunk of the application, which includes the content of the @defer
block.
Now, for a bit of tech drama, we reload the app, clear the Network tab, and then deliberately blocking network requests in the browser. Upon clicking the button, Angular starts loading the content of the @defer
and displaying the @loading
block. But, we know that the loading will fail due to the network connection, so Angular shows the content of the @error
block.
Declarative triggers
As logical expressions, @defer
supports the following declarative trigger types:
On interaction
Angular renders the on interaction
block when the user interacts with the @placeholder
block. This interaction can include actions like clicking, touching, focusing, or input events.
@defer (on interaction) {
<span>Clicked</span>
}
@placeholder {
<span>Click me!</span>
}
on hover
Angular displays the on hover
block when the user hovers over its @placeholder
block.
@defer (on hover) {
<span>Hovered</span>
}
@placeholder {
<span>Hover me!</span>
}
on idle
Angular displays the on idle
block when the browser reaches an idle state after the page has finished loading.
@defer (on idle) {
<span>Browser has reached an idle state</span>
}
on timer
The on timer
block is displayed once the specified time has passed.
@defer (on timer(5s)) {
<span>I am visible after 5s</span>
}
@placeholder {
<span>Placeholder</span>
}
on viewport
Angular displays the on viewport
block when the placeholder enters the browser’s viewport.
@defer (on viewport) {
<span>I am only visible when my placeholder is within the viewport</span>}
@placeholder {
<span>Placeholder</span>
}
Resource Prefetching
Another valuable feature for deferred loading is the capability to pre-fetch dependencies in advance of a user’s interaction. This is particularly advantageous for minimizing delays when a deferred block becomes active. The prefetch syntax operates in conjunction with the primary defer condition and employs triggers (when and/or on), similar to defer.
@defer (on interaction; prefetch on hover) {
...
}
Migration ❤️
One of the remarkable aspects of Angular is their commitment to facilitating the migration processes, and this time is no different. The Angular team is actively developing a schematic to assist us in transitioning our existing code to the new syntax. This schematic is expected to be accessible once the new version is officially released.
Additionally, according to the RFCs and feedback from the Angular team, structural directives will remain a fundamental part of Angular, ensuring that we can continue using them just as we do today.
Conclusion
In this article, I introduced you to the cool new stuff in Angular 17. We talked about how those deferred blocks do their thing and how you can play them around with conditions. I hope you found my tutorial helpful and not as confusing as trying to assemble IKEA’s furniture with vague instructions!
Coming up next, we’ll dive into the sequel of this article series and explore the other new control flow features like @if
, @else
, @switch
, and @case
blocks in Angular 17.