Введение: Знакомая боль каждого Angular-разработчика
Вы когда-нибудь тратили час на поиск ошибки, которая пряталась буквально в одной строчке кода? Если вы работаете с Angular Material Autocomplete и столкнулись с сообщением Can't bind to 'formControl' since it isn't a known property of 'input' — вы не одиноки. Этот вопрос на Stack Overflow собрал тысячи просмотров, и не зря. Ошибка вылетает в самый неожиданный момент, когда кажется, что всё уже работает идеально.
В этой статье мы разберём причины возникновения ошибки, детально сравним три подхода к её исправлению и покажем рабочие примеры на TypeScript. После прочтения вы сможете не только починить автокомплит, но и глубже понять архитектуру Angular. Поехали!
Почему возникает ошибка Can't bind to 'formControl'?
Давайте сразу к делу. Ошибка появляется, когда Angular пытается привязать директиву formControl к элементу <input>, но не находит её в текущем модуле. Звучит логично, но дьявол кроется в деталях.
На самом деле, formControl — это селектор директивы, которая является частью модуля ReactiveFormsModule. Если вы забыли импортировать этот модуль или импортировали его неправильно, Angular просто не знает, что делать с атрибутом [formControl] на вашем инпуте.
Вот типичная ситуация: вы создаёте новый компонент с автокомплитом, добавляете FormControl в TypeScript код, а в шаблоне пишете:
<input type="text" [formControl]="myControl" [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let option of filteredOptions | async" [value]="option">
{{option}}
</mat-option>
</mat-autocomplete>
И тут — бам! Красная ошибка в консоли. Знакомо? Тогда читаем дальше.
Подход 1: Импорт ReactiveFormsModule (Самый простой и правильный)
Первый и самый очевидный способ — убедиться, что ReactiveFormsModule импортирован в нужный модуль. Для standalone-компонентов (Angular 14+) или классических модулей процедура немного отличается.
Для классических модулей (NgModule)
Откройте файл модуля, где зарегистрирован ваш компонент (например, app.module.ts или shared.module.ts), и добавьте импорт:
import { ReactiveFormsModule } from '@angular/forms';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatInputModule } from '@angular/material/input';
@NgModule({
imports: [
ReactiveFormsModule,
MatAutocompleteModule,
MatInputModule,
// другие импорты
],
// ...
})
export class AppModule { }
Важно: Без MatInputModule директива matInput тоже не сработает, и вы получите похожую ошибку. Импортируйте все три модуля вместе.
Для standalone-компонентов (Angular 14+ и Angular 17)
Если вы используете новую архитектуру, импорт делается прямо в компоненте:
import { Component } from '@angular/core';
import { ReactiveFormsModule, FormControl } from '@angular/forms';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatInputModule } from '@angular/material/input';
import { AsyncPipe } from '@angular/common';
@Component({
selector: 'app-search',
standalone: true,
imports: [
ReactiveFormsModule,
MatAutocompleteModule,
MatInputModule,
AsyncPipe
],
template: `
<input type="text" [formControl]="myControl" [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let option of filteredOptions | async" [value]="option">
{{option}}
</mat-option>
</mat-autocomplete>
`
})
export class SearchComponent {
myControl = new FormControl('');
options: string[] = ['Python', 'TypeScript', 'Angular', 'React'];
filteredOptions = this.myControl.valueChanges.pipe(
// фильтрация
);
}
Этот подход — золотой стандарт. Он решает проблему в 99% случаев. Но что если ошибка всё ещё появляется?
Подход 2: Проверка версии Angular и совместимости пакетов
Иногда разработчики устанавливают Angular Material версии, несовместимой с текущей версией Angular. Например, вы используете Angular 15, а Material поставили 16-й версии. Это частая причина неожиданных ошибок, включая нашу.
Проверьте файл package.json:
"@angular/core": "^17.0.0",
"@angular/forms": "^17.0.0",
"@angular/material": "^17.0.0"
Все основные пакеты должны быть одной мажорной версии. Если у вас разнобой, выполните команду:
ng update @angular/material @angular/cdk
Это обновит Material до совместимой версии. После обновления перезапустите сервер разработки (ng serve).
Бонусный совет: Если вы используете Angular 17, обратите внимание на новый синтаксис @for вместо *ngFor. Хотя старый синтаксис всё ещё работает, новый может быть предпочтительнее.
Подход 3: Использование FormControlDirective и ручная регистрация
Этот метод — для экстремальных случаев, когда обычные импорты не помогают (например, из-за конфликтов ленивой загрузки модулей). Вы можете вручную импортировать директиву FormControlDirective и добавить её в declarations или imports вашего модуля.
В app.module.ts:
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { FormControlDirective } from '@angular/forms';
@NgModule({
imports: [
FormsModule,
ReactiveFormsModule,
// ...
],
// Иногда требуется явно задекларировать
// Но в 99% случаев это избыточно
})
export class AppModule { }
На практике этот подход почти не используется, но знание о нём может спасти вас в legacy-проектах с запутанной модульной структурой.
Сравнение подходов: что выбрать?
Давайте сведём всё в наглядную таблицу:
- Подход 1 (ReactiveFormsModule) — универсальное решение. Подходит для 95% случаев. Просто, надёжно, рекомендуется официальной документацией.
- Подход 2 (Проверка версий) — обязателен, если первый не сработал. Исключает конфликты пакетов.
- Подход 3 (FormControlDirective) — резервный вариант для сложных архитектур. Используйте только если первые два не помогли.
Блоги
Рекомендаций по книгам
Читайте также