What?
This error is a common occurrence regardless of your experience as an Angular developer. It’s not uncommon to have forms working in one area of your application only to find they don’t work in another. Perhaps you’re eager to build your template form. You create your component’s template, wire up the form controls and suddenly you’re met with this warning.
Can’t bind to ‘formGroup’ since it isn’t a known property of ‘form’.
This message indicates that Angular is unable to bind the component property form
to the [formGroup]
input since it’s not a valid HTML attribute. More importantly, it’s not a known Angular input. At least not yet and that’s the root of the problem. Both the FormsModule
and the ReactiveFormsModule
make the formGroup
binding available, so the solution is to import the necessary forms module in the appropriate containing module.
It’s all about the Modules
Surprisingly, this is not a Form problem, per se, but a module declaration problem. Modules are a powerful mechanism that allow you to manage your application dependencies through dependency injection and that’s where this error originates. You need to provide the appropriate module and import in the correct module that’s utilizing the Angular Forms directives.
A discerning reader might wonder why there isn’t a forRoot()
method for the forms module since both Forms modules declare providers. Often providers are utilized as singletons, as in there’s only ever one instance of a provider or service running during an application’s lifetime. When modules declare components, pipes, directives and providers, a forRoot()
method in the module registers the providers once. Then the components, pipes, and directives are registered as needed with a forChild()
method or something similar. In this case, both form modules declare providers whose persistence is directly related to the components that use them. Their state exists only for that component and is not shared across the application, which removes the need to utilize the forRoot()
pattern.
Form Module Types
Where does the Form module go?
If you have a component that’s declared in another module, you may see an error when trying to use Angular form controls in that template. Even if you’ve imported a form module at the root of your application, you’ll need to do it again in modules that declare their components. The form modules export their directives and must be imported in the same module that the component is declared.
Let’s look at an example application with two modules. The ReactiveFormsModule
is already imported in the root app.module.ts
, and the form is working correctly in the app.component.html
. Now it’s time to add another template form in the settings-form.component.html
, and that’s when we run into the error.
. ├── .vscode │ └── settings.json ├── src │ ├── app │ │ ├── settings │ │ │ ├── settings-form │ │ │ │ ├── settings-form.component.css │ │ │ │ ├── settings-form.component.html // [formGroup] does NOT work │ │ │ │ └── settings-form.component.ts │ │ │ └── settings.module.ts │ │ ├── app.component.css │ │ ├── app.component.html // [formGroup] works │ │ ├── app.component.ts │ │ ├── app.module.ts // import: [ReactiveFormsModule] │ │ └── shared.service.ts ├── angular.json ├── package.json └── tsconfig.json
When a component is resolving other components, pipes and directives, Angular’s dependency injection will not traverse upwards beyond the module that declares the component which, in this example, is the settings.module.ts
. To make the form directives available to your component in this example, import the appropriate Forms module in the module containing the component. Which would be the settings-form.module.ts
.
. ├── .vscode │ └── settings.json ├── src │ ├── app │ │ ├── settings │ │ │ ├── settings-form │ │ │ │ ├── settings-form.component.css │ │ │ │ ├── settings-form.component.html // [formGroup] works │ │ │ │ └── settings-form.component.ts // import: [ReactiveFormsModule] │ │ │ └── settings.module.ts │ │ ├── app.component.css │ │ ├── app.component.html // [formGroup] works │ │ ├── app.component.ts │ │ ├── app.module.ts // import: [ReactiveFormsModule] │ │ └── shared.service.ts ├── angular.json ├── package.json └── tsconfig.json
Take a look at the following example and experiment with commenting out the ReactiveFormsModule
in the SettingsModule
found at src/app/settings/settings.module.ts:
Summary
- Import a Forms module in the module that declares the component
- Import a module that exports the Forms module where the component is declared
It should make more sense what the original error message is trying to explain. Angular is trying to bind to an unknown property of the <form />
element. The [formGroup]
input is provided by Angular and is made available through Modules and Dependency Injection. The component starts its search for the corresponding Forms module in its decorator and ends at the module where it’s declared. Ultimately, one of the form modules needs to be imported at this level or be exported from another imported module. Ironically the solution to this form error doesn’t have much to do with forms. Arriving at this solution requires an understanding of modules and their dependencies, which will improve your ability to debug future problems with more certainty.