Angular v18 recently introduced a new feature to easily define a fallback content in ng-content
elements. In this article, I will discuss about it.
However, before diving into it, I will also explore how it was already possible to achieve the same result in Angular v17 or earlier versions.
When building flexible and reusable UI components, content projection stands as a fundamental tool at your disposal. It is a pattern in which you insert, or project, the content you want to use inside another component.
When configuring a component, defining a fallback content can be useful to ensure something is displayed if no content is provided.
Define fallback content in ng-content elements
Up until Angular v18, there was not a built-in support to fallback content for ng-content
. However, this didn’t imply there was no workaround.
Let’s take a look at one of the potential implementation strategies:
import { Component, ElementRef, computed, viewChild } from '@angular/core';
@Component({
standalone: true,
selector: 'my-component',
template: `
@if(!hasContent()){
<div> Default content </div>
}
<div #wrapper>
<ng-content></ng-content>
</div>
`,
})
export class MyComponent {
wrapper = viewChild.required<ElementRef<HTMLDivElement>>('wrapper');
hasContent = computed(
() => this.wrapper().nativeElement.innerHTML.length > 0
);
}
Code language: TypeScript (typescript)
The idea behind it is to wrap the ng-content
within an element, and utilize the viewChild
API to determine whether there is projected content or not.
Then, based on that, the fallback content is displayed using the @if
API provided by the new control flow, or the ngIf
directive.
Note: in older versions of Angular you may need to refactor this a bit, but I hope the concept of this strategy is clear. If not please feel free to contact me 🫶🏻
Angular v18: fallback content in ng-content
Starting from Angular v18, implementing a fallback content in ng-content
elements is extremely easier.
You just need to put the fallback content inside the ng-content
tag:
import { Component } from '@angular/core';
@Component({
standalone: true,
selector: 'my-component',
template: `
<ng-content select="header"> Default header </ng-content>
<ng-content> Default main content </ng-content>
<ng-content select="footer"> Default footer </ng-content>
`,
})
export class MyComponent {}
Code language: TypeScript (typescript)
As you can see, this new feature allows you to easily define a dedicated fallback content for each ng-content
element in your component.
So, considering the following example:
<my-component>
<footer> New footer </footer>
</my-component>
Code language: HTML, XML (xml)
The resulting template will be:
Default header
Default main content
<footer> New footer </footer>
Code language: HTML, XML (xml)
Note: before Angular v18, this would result in an error being thrown ❌
Fallback content strategies limitations
It is important to highlight that both strategies share the same limitation: Angular content projection is evaluated on component creation.
This means that if you render the projected content conditionally, for example using an @if
condition or the ngIf
directive, the fallback content will not be displayed, even if the associated condition is set to false
.
This limitation can be easily overcome, with different solutions depending on the specific use case you might have, but it’s important to remember about it when implementing the strategies I’ve presented.
Thanks for reading so far 🙏
I’d like to have your feedback so please feel free to contact me for any. 👏
Then, if you really liked it, share it among your community, tech bros and whoever you want. 👋😁