两者都查询对子元素/组件的引用,但它们在不同的地方查找:@ViewChild 查询组件的自己的模板,而 @ContentChild 查询通过 <ng-content> 组件中的内容。
@Component({
template: `
<input #nameInput /> <!-- a template reference in MY template -->
<app-child></app-child>
`,
})
export class ParentComponent implements AfterViewInit {
@ViewChild("nameInput") input!: ElementRef; // by template ref
@ViewChild(ChildComponent) child!: ChildComponent; // by component type
ngAfterViewInit() {
this.input.nativeElement.focus(); // available after the VIEW initializes
this.child.doSomething(); // call a child component's method
}
}
@ViewChild 访问组件本身在其模板中声明的元素/组件 — 在 ngAfterViewInit 中可用。
@Component({
selector: "app-card",
template: `<div class="card"><ng-content></ng-content></div>`, // content projected here
})
export class CardComponent implements AfterContentInit {
@ContentChild(CardTitleComponent) title!: CardTitleComponent;
ngAfterContentInit() {
// the projected content is ready here (EARLIER than ngAfterViewInit)
console.log(this.title);
}
}
<!-- parent projects content INTO app-card -->
<app-card>
<app-card-title>Hello</app-card-title> <!-- this is what ContentChild finds -->
</app-card>
@ContentChild 访问父组件传入的内容(通过 <ng-content> 投影)— 在 ngAfterContentInit 中可用。
@ViewChild → elements in THIS component's own template → ngAfterViewInit
@ContentChild → elements PROJECTED in from the parent → ngAfterContentInit
(plural: @ViewChildren / @ContentChildren return a QueryList of all matches)
@ViewChildren(ItemComponent) items!: QueryList<ItemComponent>; // all matching items
ngAfterViewInit() { this.items.forEach(i => ...); this.items.changes.subscribe(...); }
input = viewChild<ElementRef>("nameInput"); // signal-based query (newer Angular)
区分 @ViewChild(您的模板)和 @ContentChild(投影内容)— 以及它们各自的生命周期时序(ngAfterViewInit vs ngAfterContentInit)— 在构建需要与自己的模板元素或消费者投影到其中的内容进行交互的可复用组件时至关重要。
这是一个常见的困惑点,也是在编写组件库、标签页、表单控件和必须与子元素协调的包装器时经常需要处理的问题。