Mastering Content Projection in Angular: Beyond the Basics
In modern web development, reusability is king. You don't want to write a new Modal component for every single alert in your application. You want one ModalWrapper that can display a login form, a success message, or a video player depending on the context. In Angular, the key to unlocking this flexibility is Content Projection.
The Problem with @Input() for HTML
Beginners often try to pass content into components using @Input() strings.
// BAD PRACTICE
<app-card [title]="'My Title'" [body]="'Some long HTML string...'"></app-card>This approach becomes messy quickly. You lose syntax highlighting, you can't easily include other components (like buttons or icons) inside the string, and it's hard to maintain. Content projection solves this by allowing you to write normal HTML between the opening and closing tags of your component.
Single-Slot vs. Multi-Slot Projection
Single-slot is the default behavior. If you place one <ng-content> tag in your component, Angular will take everything from inside the parent's component tags and dump it there.
Multi-slot projection allows for granular control. By adding a select attribute to <ng-content>, you create specific "drop zones".
Child Template
<header>
<ng-content select=".card-header"></ng-content>
</header>
<main>
<ng-content></ng-content>
</main>Defines specific slots. Note: the second one has no selector, so it catches everything else.
Parent Usage
<app-card>
<h1 class="card-header">Title</h1>
<p>This goes to main.</p>
</app-card>Content is distributed based on the class selector.
Advanced: The `ngProjectAs` Attribute
Sometimes, you want to wrap an element in a container (like an ng-container) for structural reasons, but you still want it to be projected into a specific slot. The ngProjectAs attribute allows you to "disguise" an element so it matches a selector.
<ng-container ngProjectAs="[card-title]">
<h1>My Title</h1>
</ng-container>Pro Tip: Content Projection happens during the component creation phase. This means the content is initialized before the view is checked. Use thengAfterContentInitlifecycle hook if you need to access projected content programmatically using@ContentChild.