Что такое ViewChild и ContentChild?

ViewChild и ContentChild — это декораторы в Angular, позволяющие получить доступ к DOM-элементам, компонентам или директивам внутри шаблона компонента. Они используются для получения ссылок на дочерние элементы, чтобы программно взаимодействовать с ними (например, вызвать методы, изменить состояние или получить доступ к DOM).

@ViewChild

Что делает

Позволяет получить доступ к элементу из собственного шаблона компонента (то есть внутри <template> текущего компонента).

Когда применяется

  • Для получения ссылки на DOM-элемент.

  • Для доступа к экземпляру дочернего компонента или директивы.

  • Для доступа к TemplateRef или ViewContainerRef.

Когда доступен

ViewChild доступен после инициализации вьюхи — в ngAfterViewInit().

Синтаксис

@ViewChild('templateVar') elementRef!: ElementRef;

Или:

@ViewChild(ChildComponent) child!: ChildComponent;

Пример 1: DOM-элемент

&lt;input #myInput /&gt;
@ViewChild('myInput') inputElement!: ElementRef;
ngAfterViewInit() {
this.inputElement.nativeElement.focus();
}

Пример 2: Дочерний компонент

&lt;app-child #childRef&gt;&lt;/app-child&gt;
@ViewChild('childRef') childComponent!: ChildComponent;
ngAfterViewInit() {
this.childComponent.someMethod();
}

Пример 3: По типу компонента

@ViewChild(ChildComponent) childComponent!: ChildComponent;

Это удобно, если в шаблоне есть только один компонент такого типа.

@ViewChildren

Если нужно получить несколько компонентов или элементов:

@ViewChildren(ChildComponent) children!: QueryList&lt;ChildComponent&gt;;
ngAfterViewInit() {
this.children.forEach(child => child.someMethod());
}

@ContentChild

Что делает

Позволяет получить доступ к контенту, переданному через ng-content из родительского компонента.

Когда применяется

  • Для доступа к элементам, переданным в <ng-content>.

  • Используется в компонентах, которые реализуют контентную проекцию.

Когда доступен

ContentChild доступен после ngAfterContentInit().

Синтаксис

@ContentChild(ChildDirective) childDirective!: ChildDirective;

Или:

@ContentChild('projectedRef') template!: TemplateRef&lt;any&gt;;

Пример: Контентная проекция

Родительский компонент:

&lt;app-wrapper&gt;
&lt;p #projected&gt;Hello&lt;/p&gt;
&lt;/app-wrapper&gt;

Компонент с проекцией:

&lt;ng-content&gt;&lt;/ng-content&gt;
@ContentChild('projected') content!: ElementRef;
ngAfterContentInit() {
console.log(this.content.nativeElement.textContent); // "Hello"
}

Отличие ViewChild и ContentChild

Характеристика ViewChild ContentChild
Что ищет Внутри собственного шаблона компонента Внутри <ng-content> — проецируемый контент
--- --- ---
Когда становится доступен После ngAfterViewInit() После ngAfterContentInit()
--- --- ---
Тип доступа DOM, компонент, директива DOM, компонент, директива
--- --- ---
Контекст поиска Внутри текущего компонента Внутри контента, переданного снаружи
--- --- ---

Почему важно вызывать в нужных lifecycle hooks

  • @ViewChild — не следует обращаться к элементу в ngOnInit, потому что шаблон еще не отрисован. Используйте ngAfterViewInit.

  • @ContentChild — используйте в ngAfterContentInit, чтобы быть уверенным, что контент уже вставлен в DOM.

Пример полного использования

@Component({
selector: 'app-child',
template: \`&lt;p&gt;Child component&lt;/p&gt;\`
})
export class ChildComponent {
sayHello() {
console.log('Hello from child');
}
}
@Component({
selector: 'app-parent',
template: \`
&lt;app-child #viewRef&gt;&lt;/app-child&gt;
&lt;ng-content&gt;&lt;/ng-content&gt;
\`
})
export class ParentComponent implements AfterViewInit, AfterContentInit {
@ViewChild('viewRef') viewChild!: ChildComponent;
@ContentChild('contentRef') contentChild!: ElementRef;
ngAfterViewInit() {
this.viewChild.sayHello();
}
ngAfterContentInit() {
console.log(this.contentChild.nativeElement.textContent);
}
}

Модификаторы и параметры

  • { static: true } — нужен, если вы хотите получить доступ к ViewChild/ContentChild в ngOnInit.

    • Обычно используется, если элемент не зависит от *ngIf, *ngFor и других структурных директив.

    • Если зависит — используйте { static: false }.

Пример:

@ViewChild('el', { static: true }) element!: ElementRef;

Частые ошибки

  • Использование @ViewChild до ngAfterViewInit (например, в constructor или ngOnInit) — объект будет undefined.

  • Ожидание, что @ContentChild найдет элемент, который на самом деле является частью шаблона — тогда нужно использовать @ViewChild.

  • Отсутствие #templateRef в шаблоне, когда указана ссылка по строке.

  • Неправильная установка static: true/false.

@ViewChild и @ContentChild дают точный контроль над DOM и дочерними компонентами, особенно в компонентах с динамической логикой, библиотечных компонентах и при работе с ng-content. Их правильное использование требует знания архитектуры компонента и понимания порядка инициализации шаблона.