惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - Franky
N
Netflix TechBlog - Medium
Google Online Security Blog
Google Online Security Blog
月光博客
月光博客
量子位
酷 壳 – CoolShell
酷 壳 – CoolShell
V
V2EX
腾讯CDC
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 聂微东
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
M
MIT News - Artificial intelligence
Vercel News
Vercel News
The GitHub Blog
The GitHub Blog
Hugging Face - Blog
Hugging Face - Blog
博客园 - 【当耐特】
Apple Machine Learning Research
Apple Machine Learning Research
aimingoo的专栏
aimingoo的专栏
博客园 - 三生石上(FineUI控件)
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
MongoDB | Blog
MongoDB | Blog
H
Help Net Security
The Cloudflare Blog
Blog — PlanetScale
Blog — PlanetScale
F
Full Disclosure
G
Google Developers Blog
罗磊的独立博客
Jina AI
Jina AI
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Y
Y Combinator Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
J
Java Code Geeks
A
About on SuperTechFans
IT之家
IT之家
大猫的无限游戏
大猫的无限游戏
S
SegmentFault 最新的问题
有赞技术团队
有赞技术团队
GbyAI
GbyAI
雷峰网
雷峰网
T
The Blog of Author Tim Ferriss
The Register - Security
The Register - Security
U
Unit 42
D
Docker
Martin Fowler
Martin Fowler
L
LINUX DO - 热门话题
NISL@THU
NISL@THU
阮一峰的网络日志
阮一峰的网络日志
C
Cybersecurity and Infrastructure Security Agency CISA
博客园_首页
Google DeepMind News
Google DeepMind News

博客园 - dudu

.NET CQRS 的实现中引入 ReadOnlyRepository 初试 .NET CQRS 开源库 LiteBus 什么是 Agentic ? 初试 Microsoft Agent Framework 初识 Microsoft Agent Framework:一句话介绍 ASP.NET Core 中读取 UserAgent 的正确姿势 记录一下对 ASP.NET Core Middleware 进行单元测试的代码 C# 实现通用的 IdEqualityComparer 用 Angular Signal Inputs 完成一个组件的重构 量子网络操作系统 QNodeOS 资料收集 Kubernetes 集群上部署 Open WebUI 在 Kubernetes 集群的 GPU 节点上部署 Ollama 尝试在 Kubernetes 集群上用阿里云 GPU 实例部署 Ollama + DeekSeek-R1 阿里云 GPU 实例云服务器本地部署 DeepSeek R1 尝试使用阿里云计算巢部署 DeepSeek-R1 Angular 中依赖注入问题造成 Observable 订阅不更新 园子博客后台 Angular 升级:手工迁移至 Standalone Component 园子博客后台升级至 angular 19 后 eslint 9 迁移记录 学习大模型(LLM)的英文好文收集
Angular 中使用 ChildContent 记录
dudu · 2025-01-08 · via 博客园 - dudu

记录一下学习使用 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}}