I am using Angular Reactive forms. My parent component has a form, an Add Form
. Submit
and Reset
buttons. When I click on Add form
it adds a child form called profile-form
to the DOM. The profile-form
has two fields First Name
and Email
. The idea is to reuse the profile-form
component for any number of times, the add button is clicked. Each form needs to be validated and the parent form should know the validation status of each child form.
My parent form has this HTML to generate the child forms in a for loop.
<div *ngFor="let fg of formList.controls; let infoIndex = index">
<app-profile-form formControlName="fg" [formList]="formList"
[formIndex]="infoIndex"></app-profile-form>
</div>
When I look at the console, I seem to be getting an error that formControlName 'fg'
is not found in formList.controls
. How do I fix this mapping of formControls between my parent and child form, such that the validation works?
Lets try to analyze the form.
You expect below as the end value of the form
{
"formList": [
{
"firstName": "",
"email": ""
},
{
"firstName": "",
"email": ""
},
{
"firstName": "",
"email": ""
}
]
}
In the above we have
form => FormGroup : form
formList => FormArray : formList
1 => FormControl with value {email: '', firstName} : 1
2 => FormControl with value {email: '', firstName} : 2
3 => FormControl with value {email: '', firstName} : 3
So in the form we have to have this structure for the form to work
<form [formGroup]="signupForm" (ngSubmit)="submit()">
<ng-container formArrayName='formList'>
<div *ngFor="let fg of formList.controls; let infoIndex = index">
<app-profile-form [formControlName]="infoIndex" [formIndex]="infoIndex"></app-profile-form>
</div>
</ng-container>
<button>Sign Up</button>
<button type="button" (click)="resetForm()">Reset</button>
</form>
Some other amendments will include
Changing formListGroupDef
return this.formBuilder.control(
{
firstName: "",
email: ""
},
Validators.required
);
SignUpForm
this.signupForm = this.formBuilder.group({
formList: this.formBuilder.array([
this.formListGroupDef()
])
});
I have also made a few changes in your ProfileFormComponent
export class ProfileFormComponent implements ControlValueAccessor, OnDestroy {
@Input() formIndex: any;
destroyed$ = new Subject<any>();
form: FormGroup;
get firstNameControl() {
return this.form.controls.firstName;
}
get emailControl() {
return this.form.controls.email;
}
constructor(private formBuilder: FormBuilder) {
this.form = this.formBuilder.group({
firstName: ["", Validators.required],
email: ["", Validators.required]
});
this.form.valueChanges.pipe(
filter(({firstName, email}) => firstName.length > 0 || email.length > 0 ),
takeUntil(this.destroyed$)
).subscribe(value => {
this.onChange(value);
this.onTouched();
});
}
ngOnDestroy() {
this.destroyed$.next();
}
onChange: any = () => {};
onTouched: any = () => {};
registerOnChange(fn) {
this.onChange = fn;
}
writeValue(value) {
if (value) {
this.form.patchValue(value);
}
}
registerOnTouched(fn) {
this.onTouched = fn;
}
validate(_: FormControl) {
return this.form.valid ? null : { profile: { valid: false } };
}