You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用ng-template作为ng-content时ContentChildren无返回结果的解决办法

解决Angular中ngTemplateOutlet投影内容无法被ContentChildren捕获的问题

问题原因

你遇到的核心问题是:@ContentChildren默认仅查询组件的直接内容子节点,而通过*ngTemplateOutlet渲染的内容,是作为投影进来的<ng-container>的子节点存在的(属于间接后代),因此默认配置下无法被查询到。直接投影的tw-button是组件的直接内容子节点,所以能正常被捕获。

解决方案

方案1:开启ContentChildren的descendants选项

修改@ContentChildren的装饰器配置,添加descendants: true,使其查询所有内容子节点的后代元素:

// 组件逻辑中修改ContentChildren配置
@ContentChildren(TwButtonComponent, { descendants: true }) query!: QueryList<TwButtonComponent>;

过滤无关按钮

由于组件自身模板中也包含一个tw-button(菜单触发按钮),查询结果会混入该按钮,需要通过父容器过滤出菜单选项按钮:

  1. 给组件模板中菜单选项的容器添加类标记:
<div class="p-2 justify-center w{{menuWidth}} rounded-xl gap-2 flex flex-wrap bg-white menu-options">
    <ng-content></ng-content>
</div>
  1. 在点击事件中过滤:
@HostListener('click', ['$event.target'])
onClick() { 
  const menuOptions = this.query.filter(btn => 
    btn.nativeElement.closest('.menu-options')
  );
  console.log(menuOptions);
  this.menuVisibility();    
}

方案2:改用ViewChildren结合视图查询

ViewChildren会查询组件视图内的所有目标组件,包括*ngTemplateOutlet渲染的内容。同样需要过滤掉无关按钮:

// 替换ContentChildren为ViewChildren
@ViewChildren(TwButtonComponent) query!: QueryList<TwButtonComponent>;

@HostListener('click', ['$event.target'])
onClick() { 
  const menuOptions = this.query.filter(btn => 
    btn.nativeElement.closest('.menu-options')
  );
  console.log(menuOptions);
  this.menuVisibility();    
}

方案3:调整组件设计,直接接收模板输入

如果希望更精准地控制模板内容,可以给组件添加@Input()接收模板,替代内容投影:

  1. 组件逻辑修改:
@Input() data?: TwMenuData;
@Input() menuTemplate?: TemplateRef<any>;
hideMenu = true;

// 查询模板渲染后的按钮
@ViewChildren(TwButtonComponent) menuButtons!: QueryList<TwButtonComponent>;

menuVisibility(){      
  this.hideMenu = !this.hideMenu;    
} 

@HostListener('click', ['$event.target'])
onClick() { 
  console.log(this.menuButtons.map(x => x));
  this.menuVisibility();    
}
  1. 组件模板修改:
<tw-button [data]="data?.Button||{}"></tw-button>
<div class="relative">
    <div [class]="'absolute top'+top+' left'+left+' w'+menuWidth+ ' bg-transparent'" [hidden]="hideMenu">
        <div class="p-2 justify-center w{{menuWidth}} rounded-xl gap-2 flex flex-wrap bg-white">
            <ng-container *ngTemplateOutlet="menuTemplate"></ng-container>
        </div>
    </div>
</div>
  1. 父组件使用方式:
<tw-menu [data]="{Button:{Text:'Menu ng-template'}}" [menuTemplate]="content"></tw-menu>
<ng-template #content>
    <tw-button [data]="{Text:'Option 1'}"></tw-button>
</ng-template>

这种方式无需依赖内容投影,查询结果仅包含菜单选项按钮,逻辑更清晰。

内容的提问来源于stack exchange,提问作者Panagiotis Anagnostakis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 18:46:01