Day 12 — Reusable Components: Content Projection & ViewChild Deep Dive
📘 Day 12 — Reusable Components: Content Projection & ViewChild Deep Dive
Zero to Hero — Hands-on Angular Tutorial
Today you will learn:
- ✔️ What is Content Projection? (
<ng-content>) - ✔️ Single-slot vs. Multi-slot projection
- ✔️ Building a flexible Card Component wrapper
- ✔️ Accessing the DOM with
@ViewChild - ✔️ Interacting with child components programmatically
This day teaches you how to build library-quality, reusable UI components that don’t look hardcoded. 🧩
🟦 1. The Problem: Hardcoded Components
Imagine you want a card component.
If you hardcode the text inside ChildComponent, it’s useless for anything else.
Bad Way:
// child.component.html
<div class="card">
<h2>My Title</h2> <!-- Hardcoded ❌ -->
<p>My Content</p> <!-- Hardcoded ❌ -->
</div>
Goal: We want to pass HTML into the component from the parent.
🟩 2. Solution: ng-content (Single Slot)
ng-content acts as a placeholder. It tells Angular: “Take whatever HTML sits between my tags and put it HERE.”
Example: A Simple Card Wrapper
card.component.html:
<div class="card-styles">
<ng-content></ng-content> <!-- Angular puts your content here -->
</div>
app.component.html:
<app-card>
<!-- Everything here gets "projected" inside the card -->
<h3>User Profile</h3>
<button>Edit</button>
</app-card>
🟧 3. Multi-Slot Projection (Named Slots)
What if you want a Header, Body, and Footer?
You can split the content using select.
card-advanced.component.html:
<div class="card">
<div class="header">
<ng-content select="[header]"></ng-content>
</div>
<div class="body">
<ng-content></ng-content> <!-- Default slot (catches everything else) -->
</div>
<div class="footer">
<ng-content select="[footer]"></ng-content>
</div>
</div>
app.component.html:
<app-card-advanced>
<!-- Goes to header slot -->
<h2 header>My Product</h2>
<!-- Goes to default slot -->
<p>This is the main product description.</p>
<!-- Goes to footer slot -->
<button footer>Buy Now</button>
</app-card-advanced>
- ✔️
select="[attribute]"targets elements with that attribute. - ✔️
select=".class"targets elements with that class. - ✔️
select="tag"targets specific HTML tags.
🟥 4. Accessing Components: @ViewChild
Sometimes the Parent needs to control the Child logic (call a method), not just pass data.
Scenario: A specialized VideoPlayer component. The parent wants to click “Play”.
video-player.component.ts:
@Component({...})
export class VideoPlayerComponent {
play() {
console.log("Playing video... ▶️");
}
}
app.component.ts:
import { Component, ViewChild } from '@angular/core';
import { VideoPlayerComponent } from './video-player.component';
@Component({...})
export class AppComponent {
// Grab the child component instance
@ViewChild(VideoPlayerComponent) player!: VideoPlayerComponent;
handlePlayClick() {
// Calling a method on the child!
this.player.play();
}
}
- ✔️ @ViewChild gives you the actual TypeScript class instance of the child.
- ✔️ Allows strict control/orchestration from parent.
🟫 5. Accessing Native DOM Elements
If you need the raw HTML element (e.g., to scroll to bottom, focus input, or draw on Canvas), use ElementRef.
app.component.html:
<input #myInput type="text" placeholder="I will get focus..." />
<button (click)="focusInput()">Focus It</button>
app.component.ts:
import { Component, ViewChild, ElementRef } from '@angular/core';
export class AppComponent {
// Read the element as an ElementRef (Wrapper around native DOM)
@ViewChild('myInput') inputRef!: ElementRef<HTMLInputElement>;
focusInput() {
this.inputRef.nativeElement.focus();
this.inputRef.nativeElement.style.backgroundColor = 'yellow';
}
}
⚠️ Warning: Avoid direct DOM manipulation (like style.backgroundColor) unless necessary. Use Binding whenever possible. focus() is a valid use case.
🎉 End of Day 12 — What You Learned
Today you learned how to architect cleaner apps:
- ✔️ Content Projection lets you create “Wrapper” components (Cards, Layouts, Modals).
- ✔️ Multi-slot Projection organizes complex layouts.
- ✔️ @ViewChild lets the parent call methods on the child.
- ✔️ ElementRef gives safe access to raw DOM elements.
🧪 Day 12 Challenge
Build a “Modal Wrapper” Component.
Requirements:
- Create
ModalComponent. - Use Content Projection so the parent can put any form or text inside it.
- Add a generic “Close” button inside the modal logic.
- Parent uses
@ViewChildto callthis.modal.open()andthis.modal.close(). - Bonus: Add a backdrop (grey background) that closes the modal when clicked.