Angular fallback content in ng-content
Tags: angular, frontend, webdev, javascript
Published: 2024-05-08 17:54:25

**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:

```ts
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
  );
}
```

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](https://dev.to/davidepassafaro/angular-control-flow-the-complete-guide-1pan), 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:

```ts
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 {}
```

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:

```html
<my-component>
  <footer> New footer </footer>
</my-component>
```

The resulting template will be:

```html
Default header

Default main content

<footer> New footer </footer>
```

> **Note:** before **Angular v18**, this would result in an error being thrown ❌

![TLDR: is this a good summary? 🤭](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l0uua8521skf73qjxfvq.jpeg)

---

## 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 leave a **comment**, **like** or **follow**. 👏

Then, if you really liked it, **share it** among your community, tech bros and whoever you want. And don’t forget to **[follow me on LinkedIn](https://www.linkedin.com/in/davide-passafaro/)**. 👋😁