Question
Angular formControl Error with Material Autocomplete: Why It Happens and How to Fix It
Question
I am trying to use the Angular Material Autocomplete component in an Angular 2 project. I added the following template code:
<md-input-container>
<input mdInput placeholder="Category" [mdAutocomplete]="auto" [formControl]="stateCtrl">
</md-input-container>
<md-autocomplete #auto="mdAutocomplete">
<md-option *ngFor="let state of filteredStates | async" [value]="state">
{{ state }}
</md-option>
</md-autocomplete>
My component looks like this:
import { Component, OnInit } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { FormControl } from "@angular/forms";
@Component({
templateUrl: "./edit_item.component.html",
styleUrls: ["./edit_item.component.scss"]
})
export class EditItemComponent implements OnInit {
stateCtrl: FormControl;
states = [/* some data */];
filteredStates: any;
constructor(private route: ActivatedRoute, private router: Router) {
this.stateCtrl = new FormControl();
this.filteredStates = this.stateCtrl.valueChanges
.startWith(null)
.map(name => this.filterStates(name));
}
ngOnInit(): void {}
filterStates(val: string) {
return val
? this.states.filter((s) => new RegExp(val, "gi").test(s))
: this.states;
}
}
I get this error:
Can't bind to 'formControl' since it isn't a known property of 'input'
It seems like Angular cannot find the formControl directive. What causes this error, and how should it be fixed?
Short Answer
By the end of this page, you will understand why Angular shows the error Can't bind to 'formControl' since it isn't a known property of 'input', how FormControl is connected to Angular Reactive Forms, and which module imports are required when using Angular Material Autocomplete with a reactive form input.
Concept
formControl is not a normal HTML attribute. It is an Angular directive provided by the Reactive Forms system.
When Angular sees this:
<input [formControl]="stateCtrl">
it tries to find a directive named formControl. If the Angular module that declares your component has not imported ReactiveFormsModule, Angular does not know what formControl means, so it throws this error:
Can't bind to 'formControl' since it isn't a known property of 'input'
Why this matters
In Angular, components do not automatically get access to every directive in the framework. Directives such as:
ngIfngForngModelformControlformGroup
are made available by importing the correct Angular modules.
For reactive forms:
FormControlclass comes from@angular/formsformControldirective is provided byReactiveFormsModule
That means importing FormControl in your TypeScript file is not enough. You must also import the module that makes the template directive available.
The fix
In the Angular module that declares EditItemComponent, add:
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
declarations: [EditItemComponent],
imports: [
ReactiveFormsModule
]
})
export class YourModule {}
If you are using Angular Material autocomplete, you also need the relevant Material modules for your Angular Material version.
For older Angular Material versions shown in your example (md- prefix), that usually includes modules such as:
MdInputModuleMdAutocompleteModule
In newer Angular Material versions, md- became mat-, for example:
MatInputModuleMatAutocompleteModule
The key idea is this: class imports are for TypeScript, module imports are for templates.
Mental Model
Think of Angular templates like a workshop.
- Your component class brings the tools you create yourself.
- Angular modules bring the tools the workshop already provides.
You created this tool in code:
this.stateCtrl = new FormControl();
But in the template, you also want to use Angular's built-in formControl tool:
<input [formControl]="stateCtrl">
If ReactiveFormsModule is not imported, it is like trying to use a tool that was never delivered to the workshop. Angular looks around, cannot find it, and reports that formControl is not a known property.
Syntax and Examples
To use reactive form controls in Angular, you need two things:
- A
FormControlin the component class ReactiveFormsModuleimported in the Angular module
Core syntax
import { FormControl, ReactiveFormsModule } from '@angular/forms';
export class ExampleComponent {
nameCtrl = new FormControl('');
}
<input [formControl]="nameCtrl">
Working example
Component
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/operator/map';
@Component({
selector: 'app-example',
templateUrl: './example.component.html'
})
export class ExampleComponent {
stateCtrl = new FormControl('');
states = ['Alabama', 'Alaska', 'Arizona', 'Arkansas'];
filteredStates = this.stateCtrl.valueChanges
.startWith('')
.map(value => this.filterStates(value));
filterStates(value: string) {
return this.states.filter(state =>
state.toLowerCase().includes((value || '').toLowerCase())
);
}
}
Template
<md-input-container>
<input mdInput placeholder="Category" [mdAutocomplete]="auto" [formControl]="stateCtrl">
</md-input-container>
<md-autocomplete #auto="mdAutocomplete">
<md-option *ngFor="let state of filteredStates | async" [value]="state">
{{ state }}
</md-option>
</md-autocomplete>
Module
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { MdInputModule } from '@angular/material';
import { MdAutocompleteModule } from '@angular/material';
import { ExampleComponent } from './example.component';
@NgModule({
declarations: [ExampleComponent],
imports: [
BrowserModule,
ReactiveFormsModule,
MdInputModule,
MdAutocompleteModule
],
bootstrap: [ExampleComponent]
})
export class AppModule {}
Explanation
FormControlstores and tracks the input value.[formControl]="stateCtrl"connects the input element to that control.ReactiveFormsModuleenables Angular to understand theformControldirective in templates.- Material modules enable Material-specific directives like
mdInputandmdAutocomplete.
Step by Step Execution
Consider this small example:
nameCtrl = new FormControl('');
<input [formControl]="nameCtrl">
imports: [ReactiveFormsModule]
What happens step by step
- Angular loads the module.
- The module imports
ReactiveFormsModule. - That module registers directives such as:
formControlformGroupformControlName
- Angular creates the component instance.
- The component creates
nameCtrlas aFormControl. - Angular parses the template.
- It sees
[formControl]="nameCtrl"on the<input>. - Because
ReactiveFormsModuleis imported, Angular recognizesformControlas a valid directive. - Angular links the input element and the
FormControlobject. - When the user types, the
FormControlvalue updates. - When the control value changes,
valueChangesemits a new value.
What happens if ReactiveFormsModule is missing
- Angular loads the component template.
- It sees
[formControl]="nameCtrl". - It searches for a directive named
formControl. - None is available in the current module.
- Angular throws:
Can't bind to 'formControl' since it isn't a known property of 'input'
Real World Use Cases
Reactive form controls are used in many real applications:
Search and autocomplete
- Product search boxes
- City or country suggestions
- User mention dropdowns
- Category selectors
Forms with validation
- Registration forms
- Login forms
- Shipping address forms
- Payment details
Dynamic UI behavior
- Filter a list as the user types
- Enable or disable buttons based on input
- Show validation errors immediately
- Transform user input before submission
API-driven forms
A FormControl can listen to valueChanges and trigger logic like:
- filtering local arrays
- calling APIs for suggestions
- debouncing user input
- validating values asynchronously
Autocomplete is a very common case because the input value changes often and the UI must react immediately.
Real Codebase Usage
In real projects, developers usually use reactive forms with a few common patterns.
Module-level setup
Teams usually create a feature module and import form modules there, instead of importing everything everywhere.
imports: [CommonModule, ReactiveFormsModule]
Guarding against null or empty values
When autocomplete starts, the value may be null, '', or even an object in more advanced cases.
filterStates(value: string) {
const search = (value || '').toLowerCase();
return this.states.filter(state => state.toLowerCase().includes(search));
}
Using valueChanges for live filtering
this.filteredStates = this.stateCtrl.valueChanges
.startWith('')
.map(value => this.filterStates(value));
Validation patterns
nameCtrl = new FormControl('', Validators.required);
Early fixes during debugging
When template binding errors happen, developers typically check:
- Is the component declared in the correct module?
- Is the needed Angular module imported there?
- Is the right Material module imported?
- Are the template selectors correct for the Material version being used?
Feature-module awareness
A common real-world issue is importing ReactiveFormsModule into AppModule but using the component inside another module. The import must exist in the module that declares that component, directly or through a shared module.
Common Mistakes
Here are the most common beginner mistakes behind this error.
1. Importing FormControl but not ReactiveFormsModule
This is the exact issue in your example.
Broken
import { FormControl } from '@angular/forms';
This only makes the class available in TypeScript.
Fix
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
imports: [ReactiveFormsModule]
})
export class YourModule {}
2. Importing the module in the wrong Angular module
If EditItemComponent belongs to ItemsModule, importing ReactiveFormsModule only in AppModule may not help.
Fix
Import it in the module that declares the component.
3. Mixing template-driven and reactive forms without understanding the difference
Example
<input [(ngModel)]="name" [formControl]="nameCtrl">
This is usually a bad idea unless you specifically know why you are combining them.
Better
Use one approach consistently.
4. Forgetting Angular Material module imports
Even after fixing formControl, you may get other errors if Material modules are missing.
Fix
Import the relevant Material modules for your version.
5. Using old Material syntax with new Material docs
Your example uses old selectors:
md-input-containermdInputmd-autocompletemd-option
In newer Angular Material versions these became:
mat-form-fieldmatInputmat-autocompletemat-option
Make sure your code matches your installed Material version.
6. Assuming every binding error is about HTML
This error mentions input, but the problem is not the HTML element itself. The issue is that Angular does not know the directive attached to it.
Comparisons
Here are the related Angular concepts that are easy to confuse.
| Concept | What it is | Needed import | Typical use |
|---|---|---|---|
FormControl | TypeScript class | @angular/forms | Create and manage a control in code |
formControl | Template directive | ReactiveFormsModule | Bind an input to a FormControl |
ngModel | Template-driven forms directive | FormsModule | Simple two-way form binding |
formControlName | Directive used inside a FormGroup | ReactiveFormsModule | Bind named controls in larger forms |
FormsModule vs ReactiveFormsModule
| Module | Used for | Common directives |
|---|---|---|
FormsModule | Template-driven forms | ngModel |
ReactiveFormsModule | Reactive forms | formControl, formGroup, formControlName |
formControl vs formControlName
| Directive | When to use it |
|---|---|
[formControl] | When you already have a FormControl instance |
formControlName | When the control is inside a FormGroup |
Example of [formControl]
emailCtrl = new FormControl('');
<input [formControl]="emailCtrl">
Example of formControlName
form = new FormGroup({
email: new FormControl('')
});
<form [formGroup]="form">
<input formControlName="email">
</form>
Cheat Sheet
Quick fix for this error
If you see:
Can't bind to 'formControl' since it isn't a known property of 'input'
check this first:
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
imports: [ReactiveFormsModule]
})
export class YourModule {}
Reactive forms essentials
In the component
import { FormControl } from '@angular/forms';
nameCtrl = new FormControl('');
In the template
<input [formControl]="nameCtrl">
In the module
import { ReactiveFormsModule } from '@angular/forms';
Rules to remember
FormControlimport in TypeScript is not enough.ReactiveFormsModuleis required for theformControldirective.- Import the module where the component is declared.
- For
ngModel, useFormsModuleinstead. - For Material components, also import the correct Material modules.
- Match
md-ormat-syntax to your Angular Material version.
Common Material modules
Older Material versions:
MdInputModule
MdAutocompleteModule
Newer Material versions:
MatInputModule
MatAutocompleteModule
Safe autocomplete filter pattern
filterStates(value: string) {
const search = (value || '').toLowerCase();
return this.states.filter(state => state.toLowerCase().includes(search));
}
FAQ
Why does Angular say formControl is not a known property?
Because the formControl directive is not available in the current Angular module. Usually, ReactiveFormsModule is missing.
Is importing FormControl enough?
No. That only lets you use the FormControl class in TypeScript. The template directive still requires ReactiveFormsModule.
Should I import FormsModule or ReactiveFormsModule?
Use ReactiveFormsModule for [formControl]. Use FormsModule for [(ngModel)].
Why does this happen even though my code compiles?
TypeScript can compile your component class, but Angular template compilation fails because the directive is unavailable in the template scope.
Do I also need Angular Material imports?
Yes, for Material-specific directives like mdInput and mdAutocomplete. Those are separate from reactive forms.
Does the import go in AppModule?
Only if the component is declared there. If the component is declared in a feature module, import it in that feature module.
Is this error related to the input element itself?
No. The input element is fine. Angular just does not recognize the formControl directive attached to it.
Mini Project
Description
Build a small category search input using Angular reactive forms and Angular Material autocomplete. This project demonstrates how a FormControl is connected to an input, how valueChanges can filter results, and why the correct Angular module imports are required.
Goal
Create a working autocomplete field that filters a list of categories as the user types, without triggering the formControl binding error.
Requirements
- Create a component with a
FormControlfor the input. - Display an Angular Material autocomplete dropdown.
- Filter a local list of categories as the user types.
- Import the required Angular forms module in the correct Angular module.
- Import the required Angular Material modules for the input and autocomplete.
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Accessing Input Value from EventTarget in TypeScript
Learn why EventTarget has no value property in TypeScript and safely read values from HTML input events in Angular applications.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.