Angular 中使用 ChildContent 记录
记录一下学习使用 ChildContent 的试验代码,用的是 Angular 19。
AppComponent 是 parent component, SidebarComponent 是 child component,SidebarBlogCategoriesComponent 是 projected component 。
代码1:使用 ng-content 在 child component 中显示 projected component 的内容
// SidebarBlogCategoriesComponent
@Component({
selector: 'cnb-sidebar-blog-categories',
template: `
<h1>Hello, World!</h1>
`
})
export class SidebarBlogCategoriesComponent implements OnInit {
constructor() { }
ngOnInit() { }
}
// SidebarComponent
@Component({
selector: 'cnb-sidebar',
template: `
<p>SidebarComponent</p>
<ng-content></ng-content>
`
})
export class SidebarComponent implements OnInit {
@ContentChild(SidebarBlogCategoriesComponent) content?: SidebarBlogCategoriesComponent;
constructor() { }
ngOnInit() { }
ngAfterContentInit(): void {
console.log('content: ' + this.content);
}
}
// AppComponent
@Component({
selector: 'app-root',
imports: [SidebarComponent, SidebarBlogCategoriesComponent],
template: `
<p>AppComponent</p>
<cnb-sidebar>
<cnb-sidebar-blog-categories></cnb-sidebar-blog-categories>
</cnb-sidebar>
`
})
export class AppComponent {
}
运行时页面显示结果:
AppComponent
SidebarComponent
Hello, World!
代码2:Directive + TemplateRef 无法显示 projected component 的内容
这是园子博客后台从 angular 15 升级到 angular 19 后遇到的问题,详见 https://q.cnblogs.com/q/151579
注:下面的代码中用 [ngTemplateOutlet]
取代了 <ng-content>
import { NgIf, NgTemplateOutlet } from '@angular/common';
import { Component, ContentChild, Directive, OnInit, TemplateRef } from '@angular/core';
// SidebarContentDirective
@Directive({
selector: '[cnbSidebarContent]',
})
export class SidebarContentDirective {
constructor(public templateRef: TemplateRef<any>) { }
}
// SidebarBlogCategoriesComponent
@Component({
selector: 'cnb-sidebar-blog-categories',
template: `
<h1>Hello, World!</h1>
`
})
export class SidebarBlogCategoriesComponent implements OnInit {
constructor() { }
ngOnInit() { }
}
// SidebarComponent
@Component({
selector: 'cnb-sidebar',
imports: [NgIf, NgTemplateOutlet],
template: `
<p>SidebarComponent</p>
<div class="sidebar-content" *ngIf="content && content.templateRef">
<ng-container [ngTemplateOutlet]="content.templateRef"></ng-container>
</div>
`
})
export class SidebarComponent implements OnInit {
@ContentChild(SidebarContentDirective) content?: SidebarContentDirective;
constructor() { }
ngOnInit() { }
ngAfterContentInit(): void {
console.log('content: ' + JSON.stringify(this.content));
}
}
@Component({
selector: 'app-root',
imports: [SidebarComponent, SidebarBlogCategoriesComponent],
template: `
<p>AppComponent</p>
<cnb-sidebar>
<cnb-sidebar-blog-categories *cnbSidebarContent></cnb-sidebar-blog-categories>
</cnb-sidebar>
`
})
export class AppComponent {
}
页面输出:
AppComponent
SidebarComponent
console.log 的输出:
content: undefined
代码3:通过 ng-template 引用变量解决 Directive + TemplateRef 的问题
解决方法来自 stackoverflow 上 Angular content projection in standalone component 问题的回答:
We cannot use a directive on ng-template since it does not fire, ng-template is a virtual element and is not rendered in the DOM, so the better option, is to just create template reference variables like #cardHeader and #cardMainContent and access these through ContentChild and directly render them on the HTML.
@ContentChild('cardMainContent') cardMainContent!: TemplateRef<any>; @ContentChild('cardHeader') cardHeader!: TemplateRef<any>;
解决问题的示例代码:
import { NgIf, NgTemplateOutlet } from '@angular/common';
import { Component, ContentChild, Directive, OnInit, TemplateRef } from '@angular/core';
// SidebarBlogCategoriesComponent
@Component({
selector: 'cnb-sidebar-blog-categories',
template: `
<h1>Hello, World!</h1>
`
})
export class SidebarBlogCategoriesComponent implements OnInit {
constructor() { }
ngOnInit() { }
}
// SidebarComponent
@Component({
selector: 'cnb-sidebar',
imports: [NgIf, NgTemplateOutlet],
template: `
<p>SidebarComponent</p>
<div class="sidebar-content" *ngIf="content && content">
<ng-container [ngTemplateOutlet]="content"></ng-container>
</div>
`
})
export class SidebarComponent implements OnInit {
@ContentChild('sidebarContent') content?: TemplateRef<any>;
constructor() { }
ngOnInit() { }
ngAfterContentInit(): void {
console.log('content: ' + JSON.stringify(this.content?.elementRef));
}
}
@Component({
selector: 'app-root',
imports: [SidebarComponent, SidebarBlogCategoriesComponent],
template: `
<p>AppComponent</p>
<cnb-sidebar>
<ng-template #sidebarContent>
<cnb-sidebar-blog-categories></cnb-sidebar-blog-categories>
</ng-template>
</cnb-sidebar>
`
})
export class AppComponent {
}
页面显示结果:
AppComponent
SidebarComponent
Hello, World!
console.log 的输出:
content: {"nativeElement":{"__ngContext__":1}}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验
2013-01-08 在Visual Studio 2012中禁用Shift+Delete快捷键的剪切操作
2006-01-08 添加/删除程序空白窗口及控制面板窗口显示异常问题的解决方法
2005-01-08 [致歉]由于有急事未能参加1月7日的技术交流会议