Introduction: The familiar pain of every Angular developer
\n\nHave you ever spent an hour searching for an error that was hiding in literally one line of code? If you work with Angular Material Autocomplete and encountered the message Can't bind to 'formControl' since it isn't a known property of 'input' — you are not alone. This question on Stack Overflow has gathered thousands of views, and for good reason. The error pops up at the most unexpected moment, when everything seems to be working perfectly.
In this article, we will analyze the causes of the error, compare three approaches to fixing it in detail, and show working examples in TypeScript. After reading, you will be able not only to fix the autocomplete but also to gain a deeper understanding of Angular's architecture. Let's go!
\n\nWhy does the error Can't bind to 'formControl' occur?
\n\nLet's get straight to the point. The error appears when Angular tries to bind the formControl directive to an <input> element but cannot find it in the current module. Sounds logical, but the devil is in the details.
In fact, formControl is a directive selector that is part of the ReactiveFormsModule. If you forgot to import this module or imported it incorrectly, Angular simply doesn't know what to do with the [formControl] attribute on your input.
Here is a typical situation: you create a new component with an autocomplete, add FormControl in the TypeScript code, and in the template you write:
<input type="text" [formControl]="myControl" [matAutocomplete]="auto">\n<mat-autocomplete #auto="matAutocomplete">\n <mat-option *ngFor="let option of filteredOptions | async" [value]="option">\n {{option}}\n </mat-option>\n</mat-autocomplete>\n\nAnd then — bam! A red error in the console. Sound familiar? Then read on.
\n\nApproach 1: Import ReactiveFormsModule (Simplest and correct)
\n\nThe first and most obvious way is to make sure that ReactiveFormsModule is imported into the correct module. For standalone components (Angular 14+) or classic modules, the procedure is slightly different.
\n\nFor classic modules (NgModule)
\n\nOpen the module file where your component is registered (e.g., app.module.ts or shared.module.ts), and add the import:
import { ReactiveFormsModule } from '@angular/forms';\nimport { MatAutocompleteModule } from '@angular/material/autocomplete';\nimport { MatInputModule } from '@angular/material/input';\n\n@NgModule({\n imports: [\n ReactiveFormsModule,\n MatAutocompleteModule,\n MatInputModule,\n // other imports\n ],\n // ...\n})\nexport class AppModule { }\n\nImportant: Without MatInputModule, the matInput directive will also not work, and you will get a similar error. Import all three modules together.
For standalone components (Angular 14+ and Angular 17)
\n\nIf you are using the new architecture, the import is done directly in the component:
\n\nimport { Component } from '@angular/core';\nimport { ReactiveFormsModule, FormControl } from '@angular/forms';\nimport { MatAutocompleteModule } from '@angular/material/autocomplete';\nimport { MatInputModule } from '@angular/material/input';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'app-search',\n standalone: true,\n imports: [\n ReactiveFormsModule,\n MatAutocompleteModule,\n MatInputModule,\n AsyncPipe\n ],\n template: `\n <input type="text" [formControl]="myControl" [matAutocomplete]="auto">\n <mat-autocomplete #auto="matAutocomplete">\n <mat-option *ngFor="let option of filteredOptions | async" [value]="option">\n {{option}}\n </mat-option>\n </mat-