父组件传入TemplateRef经ngTemplateOutlet渲染后子组件如何用@ViewChild获取内部组件
问题根本原因
这个问题是Angular的视图归属机制导致的:
- 通过
TemplateRef传入的内容,它的视图上下文和所属组件是定义模板的父组件,不是使用它渲染的子组件。 @ViewChild/@ViewChildren默认只能查询当前组件自身视图范围内、属于当前组件上下文的元素/组件,跨上下文的内容默认无法查询到。@ContentChildren仅支持查询通过<ng-content>投影进来的内容,你通过@Input传入的模板不属于投影内容,自然也查询不到。
解决方案
方案1:父组件查询后传递给子组件(改造成本最低)
父组件负责查询自身视图内的HelloComponent实例,再将结果同步给子组件即可:
- 父组件ts修改:
// parent.component.ts import { ViewChild, ViewChildren, QueryList, AfterViewInit } from '@angular/core'; import { ChildComponent } from './child.component'; import { HelloComponent } from './hello.component'; @Component({ // 原有配置不变 }) export class ParentComponent implements AfterViewInit { name = 'Angular'; @ViewChild(ChildComponent) childComp: ChildComponent; @ViewChildren(HelloComponent) helloList: QueryList<HelloComponent>; ngAfterViewInit() { // 初始化同步结果 this.childComp.hello = this.helloList; // 监听动态变化,实时同步 this.helloList.changes.subscribe(res => { this.childComp.hello = res; }) } }
- 子组件只需要把原有的
@ViewChildren改为普通输入属性即可:
// child.component.ts @Input() hello: QueryList<HelloComponent> = new QueryList<HelloComponent>();
方案2:子组件自主查询(无父组件侵入)
如果希望查询逻辑完全封装在子组件内部,不需要父组件参与,可以手动遍历嵌入视图获取组件实例:
子组件ts修改:
// child.component.ts import { ViewChild, ViewContainerRef, AfterViewChecked, ChangeDetectorRef } from '@angular/core'; @Component({ selector: 'app-child', templateUrl: './child.component.html', styleUrls: ['./child.component.css'], }) export class ChildComponent implements AfterViewChecked { @Input() template: TemplateRef<unknown>; @ViewChild('fromParent', { read: ViewContainerRef }) container: ViewContainerRef; hello: QueryList<HelloComponent> = new QueryList<HelloComponent>(); constructor(private cdr: ChangeDetectorRef) {} ngAfterViewChecked() { const helloInstances: HelloComponent[] = []; // 遍历容器内的所有嵌入视图 this.container?._embeddedViews?.forEach(view => { view.rootNodes.forEach(node => { // 匹配HelloComponent实例 if (node?.componentInstance instanceof HelloComponent) { helloInstances.push(node.componentInstance); } }) }) // 结果变化时更新QueryList if (this.hello.length !== helloInstances.length) { this.hello.reset(helloInstances); this.hello.notifyOnChanges(); this.cdr.detectChanges(); console.log('Has hello', this.hello.length > 0); } } }
内容的提问来源于stack exchange,提问作者MHogge
相关产品推荐
相关产品推荐

