Angular: Advanced Multi-Slot Content Projection with ng-content
in a Reusable Modal
When working with Angular, one of the most powerful features at your disposal is Content Projection — a technique that allows you to insert dynamic content into components. A particularly useful application of this is creating reusable UI components like modals, where you can inject custom content depending on the context in which the modal is used.
In this post, we’ll dive into how to implement multi-slot content projection using Angular ng-content
. By the end of the post, you'll know how to build a reusable modal that accepts different content in various "slots."
Understanding Content Projection
Content projection in Angular allows you to define placeholders in a component and inject external content into those placeholders. It’s achieved using the <ng-content>
directive.
Basic Content Projection
Before we jump into advanced techniques, let’s first quickly review how simple content projection works. Imagine we have a modal component:
<!-- modal.component.html -->
<div class="modal">
<div class="modal-header">
<ng-content select="[modal-header]"></ng-content>
</div>
<div class="modal-body">
<ng-content select="[modal-body]"></ng-content>
</div>
<div class="modal-footer">
<ng-content select="[modal-footer]"></ng-content>
</div>
</div>
In this example, we’re creating a modal component with 3 parts: header, body, and footer. We use <ng-content>
to project content into the appropriate sections.
Building the Reusable Modal with Multi-Slot Content Projection
Now, let’s build a reusable modal that can accept different content in its header, body, and footer. Instead of having a single <ng-content>
, we’ll use multiple ng-content
slots for more control over where the content gets inserted.
1: Create the Modal Component
Let’s start by creating a modal component that can accept dynamic content in its header, body, and footer.
<!-- modal.component.html -->
<div class="modal" *ngIf="isVisible">
<div class="modal-dialog">
<div class="modal-header">
<ng-content select="[modal-header]"></ng-content>
</div>
<div class="modal-body">
<ng-content select="[modal-body]"></ng-content>
</div>
<div class="modal-footer">
<ng-content select="[modal-footer]"></ng-content>
</div>
</div>
</div>
// modal.component.ts
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-modal',
templateUrl: './modal.component.html',
styleUrls: ['./modal.component.css']
})
export class ModalComponent {
@Input() isVisible: boolean = false;
}
2: Using the Modal Component
In your parent component, you can now inject content into the modal through the designated slots.
<!-- app.component.html -->
<app-modal [isVisible]="isModalVisible">
<div modal-header>
<h2>Custom Modal Header</h2>
</div>
<div modal-body>
<p>This is the body content of the modal.</p>
<p>You can put anything here!</p>
</div>
<div modal-footer>
<button (click)="closeModal()">Close</button>
</div>
</app-modal>
<button (click)="openModal()">Open Modal</button>
// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
isModalVisible: boolean = false;
openModal() {
this.isModalVisible = true;
}
closeModal() {
this.isModalVisible = false;
}
}
3: Styling the Modal (Optional)
You can style the modal to make it look more like a traditional modal.
/* modal.component.css */
.modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
justify-content: center;
align-items: center;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
}
.modal-dialog {
background: white;
padding: 20px;
border-radius: 8px;
width: 50%;
}
.modal-header {
font-size: 1.5em;
font-weight: bold;
}
.modal-body {
padding: 20px 0;
}
.modal-footer {
display: flex;
justify-content: flex-end;
padding-top: 10px;
}
4: Testing the Modal
When you run the app, you’ll see a button labeled “Open Modal.” When clicked, the modal appears with custom header, body, and footer content.
- The header contains a
<h2>Custom Modal Header</h2>
. - The body has two
<p>
tags. - The footer has a “Close” button.
This modal component is reusable and flexible. You can now project different content into the modal each time you use it.
Advanced Tips
1. Default Content with <ng-content>
If you want to provide default content when no content is projected, you can place default content inside the <ng-content>
tags. For example:
<!-- modal.component.html -->
<div class="modal-body">
<ng-content select="[modal-body]">
<p>This is the default body content if none is provided.</p>
</ng-content>
</div>
2. Multiple Slots without select
If you don’t want to use the select
attribute, you can have a more generic content projection with multiple slots. For example:
<!-- modal.component.html -->
<div class="modal-header">
<ng-content></ng-content>
</div>
<div class="modal-body">
<ng-content></ng-content>
</div>
However, you may lose the flexibility of controlling which part of the modal gets which content.
Conclusion
By applying multi-slot content projection, you can control exactly where each piece of content appears in your modal, making it adaptable to various use cases. Whether you’re building complex modals, dynamic forms, or any component that requires dynamic content, mastering this technique will help you create clean, maintainable, and highly reusable components in Angular.