Angular2 Error: There is no directive with "exportAs" set to "ngForm"
If you are getting this instead:
Error: Template parse errors:
There is no directive with "exportAs" set to "ngModel"
Which was reported as a bug in github, then likely it is not a bug since you might:
- have a syntax error (e.g. an extra bracket:
[(ngModel)]]=
), OR - be mixing Reactive forms directives, such as
formControlName
, with thengModel
directive. This "has been deprecated in Angular v6 and will be removed in Angular v7", since this mixes both form strategies, making it:
seem like the actual
ngModel
directive is being used, but in fact it's an input/output property namedngModel
on the reactive form directive that simply approximates (some of) its behavior. Specifically, it allows getting/setting the value and intercepting value events. However, some ofngModel
's other features - like delaying updates withngModel
Options or exporting the directive - simply don't work (...)this pattern mixes template-driven and reactive forms strategies, which we generally don't recommend because it doesn't take advantage of the full benefits of either strategy. (...)
To update your code before v7, you'll want to decide whether to stick with reactive form directives (and get/set values using reactive forms patterns) or switch over to template-driven directives.
When you have an input like this:
<input formControlName="first" [(ngModel)]="value">
It will show a warning about mixed form strategies in the browser's console:
It looks like you're using
ngModel
on the same form field asformControlName
.
However, if you add the ngModel
as a value in a reference variable, example:
<input formControlName="first" #firstIn="ngModel" [(ngModel)]="value">
The error above is then triggered and no warning about strategy mixing is shown.
I had the same problem which was resolved by adding the FormsModule to the .spec.ts:
import { FormsModule } from '@angular/forms';
and then adding the import to beforeEach:
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ FormsModule ],
declarations: [ YourComponent ]
})
.compileComponents();
}));
I faced the same issue. I had missed the forms module import tag in the app.module.ts
import { FormsModule } from '@angular/forms';
@NgModule({
imports: [BrowserModule,
FormsModule
],
Since 2.0.0.rc6:
forms: deprecated
provideForms()
anddisableDeprecatedForms()
functions have been removed. Please import theFormsModule
or theReactiveFormsModule
from@angular/forms
instead.
In short:
- If you use template-driven forms, add
FormsModule
to your@NgModule
. - If you use model-driven forms, add
ReactiveFormsModule
to your@NgModule
.
So, add to your app.module.ts
or equivalent:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; // <== add the imports!
import { AppComponent } from './app.component';
@NgModule({
imports: [
BrowserModule,
FormsModule, // <========== Add this line!
ReactiveFormsModule // <========== Add this line!
],
declarations: [
AppComponent
// other components of yours
],
bootstrap: [ AppComponent ]
})
export class AppModule { }
Failing to have one of these modules can lead to errors, including the one you face:
Can't bind to 'ngModel' since it isn't a known property of 'input'.
Can't bind to 'formGroup' since it isn't a known property of 'form'
There is no directive with "exportAs" set to "ngForm"
If you're in doubt, you can provide both the FormsModule
and the ReactiveFormsModule
together, but they are fully-functional separately. When you provide one of these modules, the default forms directives and providers from that module will be available to you app-wide.
Old Forms using ngControl
?
If you do have those modules at your @NgModule
, perhaps you are using old directives, such as ngControl
, which is a problem, because there's no ngControl
in the new forms. It was replaced more or less* by ngModel
.
For instance, the equivalent to <input ngControl="actionType">
is <input ngModel name="actionType">
, so change that in your template.
Similarly, the export in controls is not ngForm
anymore, it is now ngModel
. So, in your case, replace #actionType="ngForm"
with #actionType="ngModel"
.
So the resulting template should be (===>
s where changed):
<div class="form-group">
<label for="actionType">Action Type</label>
<select
===> ngModel
===> name="actionType"
===> #actionType="ngModel"
id="actionType"
class="form-control"
required>
<option value=""></option>
<option *ngFor="let actionType of actionTypes" value="{{ actionType.label }}">
{{ actionType.label }}
</option>
</select>
</div>
* More or less because not all functionality of ngControl
was moved to ngModel
. Some just were removed or are different now. An example is the name
attribute, the very case you are having right now.