Ошибка Angular @ViewChild: Expected 2 arguments, but got 1 — как исправить?
Причина ошибки и правильное использование @ViewChild в Angular 9+
Ошибка Expected 2 arguments, but got 1 возникает из-за того, что начиная с Angular 8 декоратор @ViewChild (и @ContentChild) требует обязательный второй аргумент — объект с опцией static. Это изменение было введено для повышения производительности и предсказуемости работы с шаблонами.
Что такое static: true и static: false?
static: false(рекомендуется по умолчанию) — означает, что элемент или компонент будет доступен только после того, как Angular завершит рендеринг представления, то есть в хукахngAfterViewInit()и позже. Это стандартный вариант для динамических элементов, условных конструкций (*ngIf) и компонентов, которые могут появляться не сразу.static: true— делает элемент доступным уже на этапеngOnInit(). Это подходит только для статических элементов, которые всегда присутствуют в шаблоне и не зависят от условий (например,#myStaticDivбез *ngIf).
Правильные примеры
1. Доступ к элементу в ngAfterViewInit (static: false)
// Шаблон: <div #myDiv>Привет</div>
import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
@Component({
selector: 'app-example',
template: '<div #myDiv>Привет</div>'
})
export class ExampleComponent implements AfterViewInit {
@ViewChild('myDiv', { static: false }) myDiv!: ElementRef;
ngAfterViewInit() {
console.log(this.myDiv.nativeElement.textContent); // "Привет"
}
}
2. Доступ к компоненту в ngOnInit (static: true)
// Шаблон: <app-child #childComp></app-child>
import { Component, ViewChild, OnInit } from '@angular/core';
import { ChildComponent } from './child.component';
@Component({
selector: 'app-parent',
template: '<app-child #childComp></app-child>'
})
export class ParentComponent implements OnInit {
@ViewChild('childComp', { static: true }) childComp!: ChildComponent;
ngOnInit() {
console.log(this.childComp); // Доступно сразу
}
}
Когда использовать static: true?
- Элемент или компонент не обёрнут в
*ngIf,*ngForили[hidden]. - Вам нужно получить доступ к нему в
ngOnInit()(редкий случай, обычно лучше использоватьngAfterViewInit). - Пример: статический заголовок страницы
<h1 #title>Заголовок</h1>.
Когда использовать static: false?
- Элемент может быть динамическим (условный рендеринг, циклы).
- Вы обращаетесь к нему в
ngAfterViewInit()или позже. - Это безопасный вариант по умолчанию — используйте его, если не уверены.
Что будет, если поставить static: true везде?
В некоторых случаях это приведёт к ошибкам. Например, если элемент скрыт через *ngIf, то в ngOnInit() он ещё не существует, и вы получите undefined. Angular также выдаст предупреждение в консоли. Поэтому лучше явно указывать правильное значение.
Итог
Ошибка решается простым добавлением второго параметра. Для большинства случаев используйте { static: false }. Только если вы уверены, что элемент статичен и нужен в ngOnInit, ставьте { static: true }.
Альтернативный подход: использование @ViewChild без static с Angular 14+
В Angular 14 и новее появилась возможность не указывать второй аргумент, если вы используете строгий режим и сигналы. Однако это работает только при определённых условиях:
// Angular 14+ с сигналами
import { Component, viewChild } from '@angular/core';
@Component({
template: '<div #myDiv>Привет</div>'
})
export class ExampleComponent {
myDiv = viewChild<ElementRef>('myDiv'); // Без второго аргумента
}
Но если вы используете классический декоратор @ViewChild, то второй аргумент всё ещё обязателен. В Angular 15+ команда Angular рекомендует переходить на новый функциональный синтаксис viewChild(), который не требует static и автоматически определяет, когда элемент доступен.
Миграция с @ViewChild на viewChild
- Замените
@ViewChild('ref', { static: false })наviewChild('ref'). - Для статических элементов используйте
viewChild.required(), если элемент гарантированно существует. - Результат — сигнал, который можно читать через
.valueили использовать в шаблоне.
Этот подход упрощает код и избавляет от необходимости думать о static. Рекомендую изучить сигналы, если вы работаете с современными версиями Angular.