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}}
posted @   dudu  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验
历史上的今天:
2013-01-08 在Visual Studio 2012中禁用Shift+Delete快捷键的剪切操作
2006-01-08 添加/删除程序空白窗口及控制面板窗口显示异常问题的解决方法
2005-01-08 [致歉]由于有急事未能参加1月7日的技术交流会议
点击右上角即可分享
微信分享提示