What causes the "control.registerOnChange is not a function" error

FormsAngular

Forms Problem Overview


I have a form using the reactive form approach. The form is created as follow in my pug:

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

Everything works fine except when I try to change the form (which is a FormArray) in the javascript part. I get the following error:

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)

My code to change the form is quite complex and I can't simplify it or reproduce it in a plunker. More than finding directly the solution (it's too difficult with so little details), I would like to understand what this error means? And what might cause this error.

I have figured out that the error occurs at [formGroup]='form' in my HTML.

Any suggestion will help.

Update I have filed an issue on angular github here and have proposed a fix here The plunker to reproduce the issue is here

Forms Solutions


Solution 1 - Forms

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".

Solution 2 - Forms

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.

Solution 3 - Forms

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" />

Solution 4 - Forms

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).

Solution 5 - Forms

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

Solution 6 - Forms

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" />

Solution 7 - Forms

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!

Solution 8 - Forms

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.

Solution 9 - Forms

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>

Solution 10 - Forms

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.

Solution 11 - Forms

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!

Solution 12 - Forms

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.');
  }
}

Solution 13 - Forms

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.

Solution 14 - Forms

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">
...

Solution 15 - Forms

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

Solution 16 - Forms

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>

Solution 17 - Forms

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>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionncohenView Question on Stackoverflow
Solution 1 - FormsAnton NikiforovView Answer on Stackoverflow
Solution 2 - FormsVladimir PrudnikovView Answer on Stackoverflow
Solution 3 - FormsAC101View Answer on Stackoverflow
Solution 4 - FormsAlexei - check CodidactView Answer on Stackoverflow
Solution 5 - FormsAtif MajeedView Answer on Stackoverflow
Solution 6 - FormsShashwat GuptaView Answer on Stackoverflow
Solution 7 - FormsJack O'BrienView Answer on Stackoverflow
Solution 8 - FormsAshutosh PandeyView Answer on Stackoverflow
Solution 9 - FormsNicolas ZozolView Answer on Stackoverflow
Solution 10 - FormsWojciech DiechtiarView Answer on Stackoverflow
Solution 11 - FormsRodView Answer on Stackoverflow
Solution 12 - FormsbttbView Answer on Stackoverflow
Solution 13 - FormsseadersView Answer on Stackoverflow
Solution 14 - Formsx-magixView Answer on Stackoverflow
Solution 15 - FormsO-9View Answer on Stackoverflow
Solution 16 - FormsWasiFView Answer on Stackoverflow
Solution 17 - FormsAmit BeckensteinView Answer on Stackoverflow