Angular Addicts

Share this post

New Angular 17 feature: deferred loading

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: deferred loading

Next level lazy-loading demonstrated by using a Signal-based and other examples

Gergely Szerovay
Oct 17, 2023
5
Share this post

New Angular 17 feature: deferred loading

www.angularaddicts.com
Share

Angular 17 will be released in the beginning of November, and it has an exciting new feature called deferred loading (RFC).

Lazy loading is a technique that enables web apps to load resources (such as scripts) only when they are needed. Instead of loading all content upfront when the page initially loads, lazy loading defers the loading of non-essential content until the user interacts with the page, scrolls, or reaches a specific point in the page.

Lazy loading enhances the user experience, as it reduces the initial page load time and ensures that users can start interacting with the app as soon as possible, while non-essential parts of the app load in the background. It reduces the required bandwidth and the server load.

In the previous Angular versions, we can lazy-load a part of the application using the Router, or we can use dynamic imports and ngComponentOutlet.

In Angular 17, the Angular team will push lazy-loading to the next level: Angular now has a @defer control block enabling lazy-loading of the content of the block. Lazy-loading also applies to the dependencies of the content of the block: all the components, directives and pipes will be lazy-loaded, too.

In this article, I demonstrate the key aspects of lazy loading in Angular 17, such as

  • how we can specify a logical expression to trigger the rendering of a deferred block

  • how we can define a declarative trigger condition to trigger the rendering (for example on hover), which trigger types are supported

  • how to show a placeholder, a loading state or an error state with additional @placeholder, @loading and @error blocks

  • how prefetching work

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-deferred-loading

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.


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

Using @defer with a logical expression

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

<div>
  <input #checkboxDefer type="checkbox" [checked]="isCheckedDefer()" (change)="isCheckedDefer.set(checkboxDefer.checked)" id="checkboxDefer"/>
  <label for="checkboxDefer">Open the network tab of the browser's developer tools, then check this checkbox to load the <strong>app-c1</strong> component</label>
</div>
<br>

@defer (when isCheckedDefer()) {
  <app-c1/>
}
@placeholder {
  <span>Placeholder</span>
}
@error {
  <span>Error</span>
}
@loading(minimum 1s) {
  <span>Loading...</span>
}

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

I added four three block types under the @defer block, so there is:

  • A @defer block, Angular renders it, when the isCheckedDefer signal's value becomes true. It contains a child component: <app-c1/>

  • A @placeholder block, it's rendered initially, before the @defer block is triggered

  • When the @defer block is triggered, Angular loads the block's content from the server. During the loading, it shows the @loading block

  • If the loading fails, Angular shows the @error block

The use of the @placeholder, @loading and @error blocks are optional, so we can use standalone @defer blocks, too.

Let's see how this code works! When we open the app, the isCheckedDefer signal is false, so the @defer block is not triggered and the content of the @placeholder block is visible:

Open the Network tab of your browser's Developer tools, and clear it.

When we check the checkbox, the isCheckedDefer signal becomes true, so Angular loads the content of the @defer block. It removes the content of the @placeholder block, and renders the content of the @loading block. I specified a minimum condition for this block, so it'll be visible for at least one second. It's also possible to specify an after condition, it allows us to set a minimum duration time to wait before Angular shows the content of the loading block. If the content of the @defer block is loaded during this duration, Angular does not show the content of the @loading block.

So the content of the @loading block is visible for one second:

Then the content of the @defer block, the <app-a1> component is shown:

In the Developer tools we can see that after checking the box, Angular loaded a new chunk of the application, containing the content of the @defer block:

Now we reload the app, clear the content of the Network tab, then block the network requests in the browser:

When we check the checkbox, the isCheckedDefer signal becomes true, so Angular loads the content of the @defer block. It removes the content of the @placeholder block, and renders the content of the @loading block.

Then loading fails with a network connection error, so Angular shows the content of the @error block:

Using @defer with a declarative trigger condition

Deferred blocks support the following declarative trigger types:

  • on interaction

  • on hover

  • on idle

  • on timer

  • on viewport

Let's create an example for all of these!

@defer on interaction

Angular renders the on interaction block, when the user interacts with its @placeholder block. An interaction can be a click, touch focus, or input events, like keypress:

@defer (on interaction) {
  <span>Clicked</span>
}
@placeholder {
  <span>Placeholder (click on it!)</span>
}

@defer on hover

Angular renders the on hover block, when the user hovers over its @placeholder block:

@defer (on hover) {
  <span>Hovered</span>
}
@placeholder {
  <span>Placeholder (hover it!)</span>
}

@defer on idle

Angular renders the on idle block, when the browser reaches an idle state after the page has been loaded:

@defer (on idle) {
  <span>Browser has reached an idle state</span>
}
@placeholder {
  <span>Placeholder</span>
}

@defer on timer

The on timer block is rendered after the specified time is elapsed:

@defer (on timer(5s)) {
  <span>Visible after 5s</span>
}
@placeholder {
  <span>Placeholder</span>
}

@defer on viewport

Angular renders the on viewport block, when the placeholder enters the browser's viewport:

@defer (on viewport) {
  <app-c2 text="The block entered the viewport"/>
}
@placeholder {
  <span>Placeholder</span>
}

After reloading the app, we can check using the inspector tool whether the content of the @placeholder block has been rendered in the DOM or not:

Now follow these steps:

  • switch to the Network tab inside your browser's developer tools

  • clear the content of the Network tab

  • scroll all the way down to the bottom of the page

As a result, Angular loads and renders the content of the @defer block (the <app-c2> component):


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

Prefetching

Next to a trigger condition, we can specify an additional prefetch condition:

@defer (on interaction; prefetch on hover) {
  <app-c3/>
}
@placeholder {
  <span>Placeholder (hover it, then click on it!)</span>
}

Reload the app, then follow these steps:

  • clear the content of the Network tab

  • hover over the placeholder in the “Prefetch“ section

As a result, Angular loads the content of the @defer block, but it's not rendered, the @placeholder remains visible:

Then we click on the placeholder, and Angular renders the prefetched block (the <app-c3> component):

Summary

In this article, I demonstrated some of the great new features of Angular 17: I showed you how the new deferred blocks work and how to specify conditions to trigger the loading and rendering these blocks' content. I hope you have found my tutorial useful!

In the second part of this article series, I'll show you how the new control flow (@if, @else, @switch and @case blocks) works in Angular 17.

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.


5
Share this post

New Angular 17 feature: deferred loading

www.angularaddicts.com
Share
Comments
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