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

Angular中@ContentChildren在<ng-template>下失效问题咨询

Angular Tree Component: @ContentChildren Not Finding Nested Components in Recursive ngTemplateOutlet

Is this behavior expected?

Absolutely, this is expected behavior. Let me break down why:

@ContentChildren is designed to query components that are content-projected into your component—meaning they're placed directly between the opening and closing tags of your component (like your first working example where you nest <app-tree> tags manually).

When you use ngTemplateOutlet to render components recursively, those child components are part of the view tree of the parent component, not content-projected into it. They're generated dynamically by the template, not passed in as external content via <ng-content>. That's why @ContentChildren can't pick them up—they're outside the scope of what it's meant to query.

Solution: Switch to @ViewChildren with Lifecycle Hooks

To fix this, you need to use @ViewChildren instead. This decorator queries components that are part of the current component's own view, including those rendered dynamically by structural directives like *ngFor or *ngTemplateOutlet.

Here's how to adjust your TreeComponent:

Updated Component Code

import { Component, Input, ViewChildren, QueryList, AfterViewInit, AfterViewChecked } from '@angular/core';

@Component({
  selector: 'app-tree',
  template: `
    <div [style.padding-left.px]="depth * 20">
      <ng-content></ng-content>
    </div>
  `
})
export class TreeComponent implements AfterViewInit, AfterViewChecked {
  private _depth = 0;
  private _needsChildUpdate = false;

  get depth(): number {
    return this._depth;
  }

  set depth(value: number) {
    if (this._depth !== value) {
      this._depth = value;
      this._needsChildUpdate = false;
    }
  }

  @Input() isRoot = false;
  @ViewChildren(TreeComponent) children!: QueryList<TreeComponent>;

  ngAfterViewInit(): void {
    if (this.isRoot) {
      this.updateChildrenDepth();
    }
  }

  ngAfterViewChecked(): void {
    // Only update if needed to avoid infinite loops
    if (!this._needsChildUpdate && this.children) {
      this.updateChildrenDepth();
    }
  }

  private updateChildrenDepth(): void {
    this.children.forEach(child => {
      if (child.depth !== this.depth + 1) {
        child.depth = this.depth + 1;
      }
    });
    this._needsChildUpdate = true;
  }
}

Key Changes:

  • Replaced @ContentChildren with @ViewChildren to query view-based child components.
  • Added ngAfterViewChecked to handle updates after the view has fully rendered (critical for recursive templates, where children might load asynchronously).
  • Added a flag _needsChildUpdate to prevent infinite loops in ngAfterViewChecked.

Updated Template

Your recursive template can stay mostly the same—just ensure the child components are rendered within the parent <app-tree>'s view (which they already are):

<ng-template #treeTemplate let-tree let-root="isRoot">
  <app-tree *ngFor="let node of tree" [isRoot]="root">
    {{ node.text }}
    <ng-container *ngTemplateOutlet="treeTemplate; context:{$implicit: node.children || []}"></ng-container>
  </app-tree>
</ng-template>
<ng-container *ngTemplateOutlet="treeTemplate; context:{$implicit: treeModel, isRoot:true}"></ng-container>

Fixing the Secondary Issue: Host Component in Query Results

Good catch about @ContentChildren including the host component itself. The good news is that @ViewChildren doesn't have this problem—it only queries components that are direct children in the view tree, not the host component itself. That means you can remove the .filter(tree => tree !== this) check entirely from updateChildrenDepth()—it's no longer needed.

Why This Works

@ViewChildren watches the component's view tree, so any components added dynamically (like via your recursive template outlet) will be detected once the view stabilizes. The ngAfterViewChecked hook ensures we update child depths after all recursive renders are complete.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:56:40