At the summit of Mount Fuji 富士山 - the sacred mountain of Japan - you will learn the highest techniques of swordsmanship. This is the ultimate trial before earning the title of Angular master!
Content Projection is like the technique of passing a sword to the next successor - it allows components to accept arbitrary content:
1// Simple slot
2@Component({
3 selector: 'app-panel',
4 template: \`
5 <div class="panel">
6 <ng-content></ng-content>
7 </div>
8 \`
9})
10export class PanelComponent {}
11
12// Usage
13<app-panel>
14 <p>Any content</p>
15</app-panel>1@Component({
2 selector: 'app-card',
3 template: \`
4 <article class="card">
5 <header class="card-header">
6 <ng-content select="[card-header]"></ng-content>
7 </header>
8
9 <main class="card-body">
10 <ng-content></ng-content>
11 </main>
12
13 <footer class="card-footer">
14 <ng-content select="[card-footer]"></ng-content>
15 </footer>
16 </article>
17 \`
18})
19export class CardComponent {}
20
21// Usage
22<app-card>
23 <h2 card-header>Samurai Profile</h2>
24
25 <p>Main card content - samurai description...</p>
26 <img src="samurai.jpg" alt="Samurai">
27
28 <div card-footer>
29 <button>Edit</button>
30 <button>Delete</button>
31 </div>
32</app-card>1@Component({
2 selector: 'app-expandable',
3 template: \`
4 <div class="header" (click)="toggle()">
5 <ng-content select="[header]"></ng-content>
6 <span>{{ isExpanded() ? '▼' : '▶' }}</span>
7 </div>
8
9 @if (isExpanded()) {
10 <div class="content">
11 <ng-content></ng-content>
12 </div>
13 }
14 \`
15})
16export class ExpandableComponent {
17 isExpanded = signal(false);
18
19 toggle(): void {
20 this.isExpanded.update(v => !v);
21 }
22}1@Component({
2 selector: 'app-tabs',
3 template: \`
4 <div class="tab-headers">
5 @for (tab of tabs(); track tab.title) {
6 <button
7 [class.active]="tab === activeTab()"
8 (click)="selectTab(tab)">
9 {{ tab.title }}
10 </button>
11 }
12 </div>
13 <div class="tab-content">
14 <ng-content></ng-content>
15 </div>
16 \`
17})
18export class TabsComponent implements AfterContentInit {
19 @ContentChildren(TabComponent) tabsList!: QueryList<TabComponent>;
20
21 tabs = signal<TabComponent[]>([]);
22 activeTab = signal<TabComponent | null>(null);
23
24 ngAfterContentInit(): void {
25 this.tabs.set(this.tabsList.toArray());
26 if (this.tabs().length > 0) {
27 this.selectTab(this.tabs()[0]);
28 }
29 }
30
31 selectTab(tab: TabComponent): void {
32 this.tabs().forEach(t => t.isActive.set(false));
33 tab.isActive.set(true);
34 this.activeTab.set(tab);
35 }
36}
37
38@Component({
39 selector: 'app-tab',
40 template: \`
41 @if (isActive()) {
42 <ng-content></ng-content>
43 }
44 \`
45})
46export class TabComponent {
47 @Input() title = '';
48 isActive = signal(false);
49}