Using Deferred Views in Angular 17 with @defer, @placeholder, @error, and @loading

Angular 17 has introduced Deferred Views which can help to drastically improve and optimize the performance of large-scale applications consisting of complex layouts. Deferred views allow us to delay the creation and rendering process of deferred sections of our template. Imagine having to wait for an entire movie to download before you could even see…

By.

min read

Angular 17 has introduced Deferred Views which can help to drastically improve and optimize the performance of large-scale applications consisting of complex layouts. Deferred views allow us to delay the creation and rendering process of deferred sections of our template.

Imagine having to wait for an entire movie to download before you could even see the opening scene. That’s what happens with traditional web apps, loading all content upfront. But what if you could load only what’s needed, when it’s needed? That’s the magic of lazy loading.

Think of it like a delicious meal served in courses. Instead of presenting everything at once, lazy loading delivers content in stages, based on your interaction. Which results in:

  • Faster initial page load: As we are not loading the whole dump of our code, users could start interacting with application while other sections can be loaded in the background or as they are requested.
  • A smoother experience: Limited resources will be consumes hence resulting in better optimized and lightening fast and seamless experience even if internet connection is slow.
  • Reduced resource usage: Only load what’s necessary, that eventually saves bandwidth and server resources.

 

How Deferred Views Different from Lazy Loading?

Angular 17 takes lazy loading to the next level with the @defer control block. This powerful feature allows us to:

  • Lazily load specific sections of our app: Focus on the essential content first and then load additional features as needed.
  • Automatic dependency loading: All components, directives, and pipes needed for the deferred section are loaded alongside it that ensures everything work smoothly.
  • Declare Trigger Conditions: Moreover, @defer blocks allow us to implement our own logic and define declarative triggers based on user interactions. We will discuss about various triggers in more details with example in further sections.

 

Deferrable views in Angular 17 provides a way to delay the loading of specific sections of our application’s template. We can define a @defer block to enclose the template section that we want to defer. Which tells Angular to hold the loading of it and it’s dependencies.

@defer {
  <product-details>...</product-details>
}

Deferrable views support a number triggers to determine when the deferred content can/should be loaded. These triggers include viewport, idle, timer etc.

 

Why Defer Views Only Supported in Standalone Components?

Yes, defer views are only supported in Angular applications developed using Standalone Components. Let’s have a quick look at why is it so:

1. Dependency Injection: As Standalone components don’t rely on the traditional Angular injector for dependency injection, the implementation of defer views gets simplified and avoids potential conflicts with existing features like view providers in non-standalone components.

2. Change Detection: With their change detection strategy, the Standalone components are independent of the main application. So that the management of the lifecycle hook of deferred views is ensured to update only when requested.

3. Template Compiling: The compilation process of Standalone components is different which allows optimizations to deferred views and doesn’t conflict with lazy loading of template code and avoiding unnecessary compilations until needed.

 

Sub Blocks Supported Deferrable Views

A @defer blocks can be used with various optional sub blocks, that helps in gracefully handling the user experience:

 

@defer Block

The content which is kept inside the @defer block is lazily loaded, whether its simple text or element or another stand alone child component. It will not load its content until a specified trigger or condition is satisfied, only after that the template is rendered to load its dependencies eventually.
By default the @defer block is rendered on the idle trigger.

 

@placeholder Block

The @placeholder block is used to show a content like ‘Place For Details’ before the defer content is triggered to load. Placeholder is helpful to indicate that a defer block is placed that will be loaded to replace. The content inside the @placeholder block is loaded normally which can have any kind of content including simple text, component, directive etc.

We can also define a minimum parameter to define a time(ms) to eliminate any odd looking fluctuation like effect. The minimum parameter will let users see and observe the placeholder message if it is defined.

@defer {
  <product-details-component />
} @placeholder (minimum 500ms) {
  <p>You Will See Complete Details Here!</p>
}

 

@loading Block

Sometimes the defer block may contain other components, that themselves load some of their dependencies asynchronously. In such use-cases we can use another optional @loading block.

The content inside the @loading block will show up until the @defer block dependencies are loaded. We can place a spinner, Loading gif or simple “Loading…” text.

@loading accepts two optional parameters:

minimum: Amount of minimum time, the loading block should be shown.

after: Amount of time to wait before showing the loading template.

Both of these can be used in seconds(s) or milliseconds(ms).

@defer {
<app-loading />
} @loading (after 500ms; minimum 2s) {
  <div>LOADING.....</div> 
}

 

@error Block

The @error block will have the content that will show up if deferred content is failed to load. This is how we can use the @error block:

@defer {
  <product-details />
} @error {
  <p>Failed to load the details</p>
}

This is how our @defer block will look if we use all of the optional blocks we discussed above:

 @defer {
    <product-details />
  }

  @placeholder (minimum 500ms) {
    <p>Product Details Will Loaded Here...</p>
  }

  @loading (after 100ms; minimum 1s) {
    <img alt="Loading..." src="loading.gif" />
  } 

  @error {
    <p>Failed to load the details</p>
  }

 

Various Triggers Supported by Deferred Views in Angular 17

We define a @defer blck that will be loaded based on some trigger. There are two options available to trigger a deferrable view i.e on and when.

 

The “on” trigger

There are number of available for on triggers which can be used as single or combination of multiple triggers with OR condition for example on hover, on timer(7s). So the @defer block can be triggered via hover or it will be trigger after a timer of 7 seconds completed.

@defer (on hover; on timer(7s)) {
  <product-details />
} @placeholder {
  <img src="placeholder.png" />
}

Various Inbuilt “on” Triggers

There are number of inbuild on triggers that can be used directly as shown below with details:

on idle

After the page is painted and browser is reached at idle state then the defer block will render

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

 

on viewport

Viewport is the visible area in browser window. If the @defer block is scrolled to the viewport, then it will be rendered and replaced by placeholder loading all its dependencies if any:

@defer (on viewport) {
   <div>You Are Now in Viewport</div>
}
@placeholder {
   <span>Placeholder</span>
}

 

on interaction

The interaction is trigger when a user click or perform keydown events.

@defer (on interaction) {
  <div>You Just Interacted!</div>
} @placeholder {
  <div>Placeholder</div>
}

on hover

A simple hover can also trigger the render of @defer block. These mainly counts the mouseenter and focusin events.

@defer (on hover) {
  <div>You Just Hovered!</div>
} @placeholder {
  <div>Hover Here!</div>
}

 

on timer

We can define the timer(5s), that would trigger the render of @defer block as shown below:

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

on immediate

The immediate trigger loads the defer blocks immediately and starts the rendering process at the same time:

@defer (on immediate) {
  <calendar-cmp />
} @placeholder {
  <div>Calendar placeholder</div>
}

 

Using Template Reference Trigger

We can also incorporate the interaction trigger from a template ref variable for on hover ,on interaction and on viewport triggers as shown below:

<div #greeting>Hello!</div>

@defer (on viewport(greeting)) {
  <greetings-cmp />
}

<button type="button" #greeting>Hello!</button>

@defer (on interaction(greeting)) {
  <calendar-cmp />
} @placeholder {
  <div>Calendar placeholder</div>
}

<div #greeting>Hello!</div>

@defer (on hover(greeting)) {
  <calendar-cmp />
} @placeholder {
  <div>Calendar placeholder</div>
}

 

The “when” trigger

The when trigger takes an expression that would return a positive Boolean to render the @defer block.

<select #selectbox (change)="onSelectboxChange($event)">
  <option value="option1">Option 1</option>
  <option value="option2">Option 2 (loads content)</option>
  <option value="option3">Option 3</option>
</select>

@defer (when selectedOption === 'option2') {
<app-option-two />
}

@placeholder {
<span>Content will appear when you select "Option 2".</span>
}

 

Conclusion

Deferrable views offer a powerful tool in Angular to optimize performance and user experience by strategically delaying the loading of content until it’s actually needed. We’ve seen how to utilize various features like placeholders, loading indicators, and error handling to create a smooth and informative loading experience for users. Remember to configure the triggers and sub-blocks based on your specific use cases to maximize the benefits of deferred loading.

Leave a Reply

Your email address will not be published. Required fields are marked *