导致“ Control.registerOnChange is not a function”错误的原因

我有一个形式使用的反应形式的方法。形式创建如下在我的哈巴狗:

form([formGroup]='form', novalidate='', (ngSubmit)='postSurvey(form.value, form.valid)')

除了在 javascript 部分中尝试更改表单(即 FormArray)之外,一切都正常。我得到以下错误:

EXCEPTION: Error in http://localhost:8080/app/components/fillForm.template.html:0:326 caused by: control.registerOnChange is not a function
core.umd.js:3497 TypeError: control.registerOnChange is not a function
at setUpControl (http://localhost:8080/node_modules/@angular/forms/bundles/forms.umd.js:1634:17)
at eval (http://localhost:8080/node_modules/@angular/forms/bundles/forms.umd.js:4752:25)
at Array.forEach (native)
at FormGroupDirective._updateDomValue (http://localhost:8080/node_modules/@angular/forms/bundles/forms.umd.js:4747:29)
at FormGroupDirective.ngOnChanges (http://localhost:8080/node_modules/@angular/forms/bundles/forms.umd.js:4616:22)
at Wrapper_FormGroupDirective.ngDoCheck (/ReactiveFormsModule/FormGroupDirective/wrapper.ngfactory.js:30:18)
at View_FillFormComponent2.detectChangesInternal (/AppModule/FillFormComponent/component.ngfactory.js:275:32)
at View_FillFormComponent2.AppView.detectChanges (http://localhost:8080/node_modules/@angular/core/bundles/core.umd.js:12592:18)
at View_FillFormComponent2.DebugAppView.detectChanges (http://localhost:8080/node_modules/@angular/core/bundles/core.umd.js:12739:48)
at ViewContainer.detectChangesInNestedViews (http://localhost:8080/node_modules/@angular/core/bundles/core.umd.js:12850:41)
at CompiledTemplate.proxyViewClass.View_FillFormComponent0.detectChangesInternal (/AppModule/FillFormComponent/component.ngfactory.js:64:14)
at CompiledTemplate.proxyViewClass.AppView.detectChanges (http://localhost:8080/node_modules/@angular/core/bundles/core.umd.js:12592:18)
at CompiledTemplate.proxyViewClass.DebugAppView.detectChanges (http://localhost:8080/node_modules/@angular/core/bundles/core.umd.js:12739:48)
at CompiledTemplate.proxyViewClass.AppView.internalDetectChanges (http://localhost:8080/node_modules/@angular/core/bundles/core.umd.js:12577:22)
at CompiledTemplate.proxyViewClass.View_FillFormComponent_Host0.detectChangesInternal (/AppModule/FillFormComponent/host.ngfactory.js:29:19)

我的代码来改变形式是相当复杂的,我不能简化它或复制它在一个柱塞。除了直接找到解决方案(这太难了,因为细节太少) ,我还想知道这个错误意味着什么?是什么导致了这个错误。

我已经发现错误发生在我的 HTML 的 [formGroup]='form'

任何建议都会有帮助。

更新 我已经在角度 github 给你上提交了一个问题,并且已经提出了一个修复方案 给你重现这个问题的活塞是 < a href = “ http://next.plnkr.co/edit/vh1HCo2WrwNgr95G? open = lib% 2Fapp.ts & amp; deferRun = 1”rel = “ noReferrer”> 在这里

111807 次浏览

Yes, that error message is a bit cryptic, but if you use FormBuilder, you would see this when you added a control to FormGroup in your component and named it "A", but then either forgot to add input with formControlName="A" to your template, or formControlName for the intended input is not A, or empty, or not present.

Basically, it says: "I cannot match the control I have in FormGroup to the control in the template".

Maybe you have moved a control element outside the group in the template.

OK:

<div formGroupName="passwordForm">
Password: <input type="password" formControlName="password">
Confirm: <input type="password" formControlName="confirmPassword">
</div>

Not OK:

Password: <input type="password" formControlName="password">
<div formGroupName="passwordForm">
Confirm: <input type="password" formControlName="confirmPassword">
</div>

I came across looking for a solution to the similar issue and then found a solution myself. My issue was the following. I had a form like this

form: FormGroup = new FormGroup({
status: new FormArray([])
});

Initially it was represented by the list of checkboxes for each status on the template. And then I created a custom component to represent status selector and used it in template like so

<status-selector [formControlName]="'status'"></status-selector>

The problem is that formControlName must point to FormControl instance, but actually it was pointing to a FormArray instance. So, changing to status: new FormControl([]) fixed this issue for me.

I have also encountered this error when mixing template driven with reactive driven approaches (by mistake):

<input #inputCtrl
[formControl]="inputCtrl"
/>

inputCtrl was properly defined in the component. Of course, #inputCtrl must be scrapped in order to work (it was hard to see when input had about 10 attributes).

If have defined a FormArray field in your form, note that you do NOT need to label it with formControlName="". You need to handle the input and validation in other ways (setters, getters, functions), but will definitely get an error if you try to assign formControlName to a FormArray!

This error also appears when we use a reactive form inside ng-template in conjunction with *ngIf.

To avoid this use ng-container and do not use ngElse.

Adding in what was causing it in my situation.

.ts file,

...


export class MyComponent {
dateFrom = new FormControl(new Date());
...
}

.html file,

  <mat-form-field>
<input matInput [matDatepicker]="dateFrom" [formControl]="dateFrom"
placeholder="Min date">
<mat-datepicker-toggle matSuffix [for]="dateFrom"></mat-datepicker-toggle>
<mat-datepicker #dateFrom ></mat-datepicker>
</mat-form-field>

Basically, within the template file, it didn't know which 'dateFrom' to choose, and chooses the one in the template file, which isn't the FormControl in my typescript file, it's the mat-datepicker element in the template file.

Renaming the FormControl to dateFromCtrl fixed this,

i.e.

...


export class MyComponent {
dateFromCtrl = new FormControl(new Date());
...
}

.html file,

  <mat-form-field>
<input matInput [matDatepicker]="dateFrom" [formControl]="dateFromCtrl"
placeholder="Min date">
<mat-datepicker-toggle matSuffix [for]="dateFrom"></mat-datepicker-toggle>
<mat-datepicker #dateFrom ></mat-datepicker>
</mat-form-field>

Works as expected.

Kodos to VS Code for figuring this out. I got pushed this direction by doing a Cmd + Click on the initial 'dateFrom' at [formControl]="dateFrom", and it pointed me to the mat-datepicker element.

In my case the error occurred when the formControl name was same as a template variable on the page. For example

<select id="status" [formControl]="userStatus">...</select>


<form-status #userStatus ></form-status> //matching template variable name

In my case this error was thrown because I was using FormControlName instead of FormArrayName to bind to a FormArray in my template.

My component code:

public form = new FormGroup({
...
checkboxes: new FormArray([....])
})

My template code that threw error:

<input type="checkbox" formControlName="checkboxes" />

Fix:

<input type="checkbox" formArrayName="checkboxes" />

to me it happened when I used same [formControl]="carBrand" and [matAutocomplete]="carBrandAuto" from my autocomplete input

I changed this

FROM:

...
<input
[formControl]="carBrand"
[matAutocomplete]="carBrand"
>
<mat-autocomplete matAutocomplete #carBrand="matAutocomplete">
...

TO

...
<input
[formControl]="carBrand"
[matAutocomplete]="carBrandAuto"
>
<mat-autocomplete matAutocomplete #carBrandAuto="matAutocomplete">
...

For future readers, my problem was a simple one and it causes TypeError: control.registerOnChange is not a function

When I was creating the new FormGroup I accidentally used FormGroup instead of FormControl

  myForm: FormGroup = new FormGroup({
lname: new FormGroup({}),
fnam: new FormGroup({}),
date: new FormGroup({}),
});

It should have been this:

  myForm: FormGroup = new FormGroup({
lname: new FormControl(''),
fnam: new FormControl(''),
date: new FormControl(null),
});

Hopefully this will save someone a few minutes in the future!

this.extensionForm = this._fb.group({
id: [''],
category: [12],
extensions: new FormArray([]),
priority: ['', []],
});
formArray.push(this.extensionForm);

Note :- error occurs because of you have used formControlName where you declare formArray, you must use formArrayName instead

<input type="text" formControlName="extensions" />


simple solution:


<input type="checkbox" formArrayName="extensions" />

In Angular I had the same problem when I tried to build my own form component and forgot to implement the ControlValueAccessor interface:

import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';


@Component({
selector: 'my-country-select',
templateUrl: './country-select.component.html',
styleUrls: ['./country-select.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CountrySelectComponent),
multi: true,
},
],
})
export class CountrySelectComponent implements OnInit, OnChanges, ControlValueAccessor {


propagateChange = (_: any) => { }; // ControlValueAccessor


private internalValue: string | undefined;


get value(): string | undefined {
return this.internalValue;
}
set value(value: string | undefined) {
this.internalValue = value;
this.propagateChange(value);
}


// some other methods here


// implementing the ControlValueAccessor interface with these three methods
writeValue(obj: any): void {
if (obj) {
this.value = obj;
}
}


registerOnChange(fn: any): void {
this.propagateChange = fn;
}


registerOnTouched(fn: any): void {
// throw new Error('Method not implemented.');
}
}

In my case I got the error when I used formControlName in the template when actual form model was a FormGroup instance. Changing to formControlGroup helped.

I had this error when I tried to access formGroup control like this

myFormGroup['controlName']

instead of using .get('controlName'). and btw If .get('...') is causing some typing problems, see this: https://stackoverflow.com/a/67835904/8094012

In my case, I was passing simple property to formControl i.e.

tagsList: Array<string> = ['banana', 'apple']


<myComponent [formControl]="tagsList"></myComponent>

but generally it should be like

tagsList = new FormControl()


<myComponent [formControl]="tagsList"></myComponent>

or

tagsList: Array<string> = ['banana', 'apple']


<myComponent [(ngModel)]="tagsList"></myComponent>

In my case the issue was that I was referring to something as a FormGroup instead of a FormControl with an object as the value.

This was my initial, faulty code:

formArray.push(
new FormGroup({
value: new FormControl('', Validators.required),
description: new FormControl('', Validators.required),
tags: new FormArray([], Validators.minLength(1)),
}),
);

and in the template, I was using a component (custom-form-component in this example) which implements ControlValueAccessor:

<li *ngFor="let item of items; let itemIndex = index"
<custom-form-component [formGroupName]="itemIndex"></custom-form-component>
</li>

This group is handled in the custom-form-component component which I'm using, therefore instead a FormControl should be used:

formArray.push(
new FormControl({
value: '',
description: '',
tags: [],
}),
);

and in the template, use formControlName instead of formGroupName:

<li *ngFor="let item of items; let itemIndex = index"
<custom-form-component [formControlName]="itemIndex"></custom-form-component>
</li>

I encountered this when I accidentally reset the value of my form control property instead of using the .setValue() method.

Wrong

control = new FormControl([1])... control = []

Right

control = new FormControl([1])... control.setValue([])