I have an Angular 15 application (see source code on Stackblitz) that uses reactive forms and a ControlValueAccessor pattern to create a parent form which includes child form groups. When I add a form group and annotate it as a FormGroup in the parent html template, the data is not passed to the child. When I annotate it as a FormControl it is passing and accessing the data of the child form correctly, but I get the error
ERROR Error: control.registerOnChange is not a function
Furthermore I can't access the individual controls inside the FormGroup if I annotate it as a FormControl.
In the code example childGroupForm2 receives the passed values but childGroupForm does not.
My optimal solution would be to annotate the FormGroup as a FormGroup and pass the values from the parent component to the child.
Does anyone know why this is not working with FormGroups but works with FormControl?
Edit for minimal code:
// parent.component.html
<form [formGroup]="parentForm">
<child-group [formGroup]="childGroupForm"> </child-group>
<child-group [formControl]="childGroupForm2"> </child-group>
</form>
// parent.component.ts
get childGroupForm(): FormGroup {
return this.parentForm.get('childGroupForm') as FormGroup;
}
get childGroupForm2(): FormControl {
return this.parentForm.get('childGroupForm2') as FormControl;
}
ngOnInit() {
this.parentForm = this.fb.group({
childGroupForm: this.fb.group({
elementInput: 'Test1',
elementsSelectionInput: 'Test2',
}),
childGroupForm2: this.fb.group({
elementInput: 'Test3',
elementsSelectionInput: 'Test4',
}),
});
}
Your child-group is a custom form control (a component that implements ControlValueAccessor). A Custom form control is "feed" with a FormControl -not with a FormGroup-.
So your Form in parent should be
this.parentForm = this.fb.group({
name: 'Test0',
childGroupForm: this.fb.control({ //<--are FormControl that "store" an object
elementInput: 'Test1',
elementsSelectionInput: 'Test2',
}),
childGroupForm2: this.fb.control({ //<--are FormControl that "store" an object
elementInput: 'Test3',
elementsSelectionInput: 'Test4',
}),
});
And you parent like
<form [formGroup]="parentForm">
<child-group formControlName="childGroupForm"></child-group>
<child-group formControlName="childGroupForm2"></child-group>
</form>
Personal opinion: A custom form control should be used to make some "more complex" that mannage a serie of inputs. You can get the same using a simple component if you use viewProvider like this SO
An example with a formGroup, you can see the stackblitz
//child
@Component({
selector: 'hello',
template: `<h1>Hello </h1>
<div [formGroupName]="group">
<input formControlName="elementInput"/>
<input formControlName="elementsSelectionInput"/>
</div> `,
styles: [`h1 { font-family: Lato; }`],
viewProviders:[{ provide: ControlContainer, useExisting: FormGroupDirective }]
})
export class HelloComponent {
@Input() group: string;
constructor(){}
}
Then the parent use
<form [formGroup]="form">
<hello [group]="'childGroupForm'" > </hello>
</form>
form=new FormGroup({
childGroupForm:new FormGroup({
elementInput:new FormControl(),
elementsSelectionInput:new FormControl()
})
})
NOTE: In the example I use new Form and new FormGroup instead of formBuilder, but it's the same