The Context
Jecelyn Yeen has an excellent article on how to organize nested forms in Angular2.
In her example, she uses an array to display multiple Address form sub-components so that the user can endlessly click "Add Address" and the form will still validate correctly. I would like to do something similar, but instead of having an array of sub-forms, instead display a different sub-form depending on the value of a dropdown.
I try to structure my code similarly to hers, except instead of returning a form builder array, I use a form group for each subform I would like to display. When a dropdown option is selected, I delete the form groups that should not be displayed, and recreate the ones that should be. The problem I'm getting is that when structured this way, the parent form does not pick up changes within the child form.
The Code
ts:
public showNameForm: boolean = false;
public showAddressForm: boolean = false;
public showPhoneNumberForm: boolean = false;
constructor (fb: FormBuilder) {
this.myForm = fb.group({
nameInfo: this.getNameForm(),
addressInfo: this.getAddressForm(),
phoneNumberInfo: this.getPhoneNumberForm()
});
}
getNameForm () {
return this.fb.group({
name: ["", Validators.required]
});
}
getAddressForm () {
return this.fb.group({
address1: ["", Validators.required],
address2: ["", Validators.required],
city: ["", Validators.required],
state: ["", Validators.required],
zipcode: ["", Validators.required]
});
}
getPhoneNumberForm () {
return this.fb.group({
phone: ["", Validators.required]
});
}
handleDropdownSelect (value) {
if(value === 1) {
this.showNameForm = true;
this.myForm.controls['nameInfo'] = this.getNameForm();
delete this.myForm.controls['addressInfo'];
delete this.myForm.controls['phoneNumberInfo'];
}
// Similar for other cases, e.g. if value is 2, only show addressInfo
}
html:
<form [formGroup]="myForm">
<div [formGroupName]="'nameInfo'" *ngIf="showNameForm">
<input type="text" formControlName="name" />
</div>
<!-- Similar for other fields -->
<!-- ... -->
<button [ngClass]="{disabled: !myForm.valid}">Submit</button>
</form>
Doing this, the submit button is always remaining invalid, even after I type all the required text. Inspecting the form object with the Chrome debugger, I see that myForm.value is equal to
{
nameInfo: {
name: ""
}
}
In other words, the text I type into the sub-form groups never gets picked up by the parent form. But I can see that myForm.controls.nameInfo does have a value with the correct information, and the same thing when the others are selected. All of these properties have a valid property of true. Yet, the parent form does not pick up these changes unless I manually call myForm.updateValueAndValidity(). This is not ideal, since I would like for the form to appear as valid as soon as it becomes valid, without having to add my own event listener to make it happen.
[ngClass]instead of the[disabled]attribute on the button? One of those actually controls whether or not the button will be disabled. The other is applying a css class to the button based on form validity. plnkr.co/edit/usLejtwhMZGjyxS6InCb?p=previewmyForm.addressInfo = this.getAddressForm()which was breaking the template. So I guess the real question is how to selectively display sub-forms and have the parent form validate correctly (seems to be coming back as invalid when the not-displayed forms have required fields that are empty, which will be the case when they're not displayed).disable()on the group you aren't displaying should do the trick. If you want to update your question and if that works I'll come back and put it as the answer tomorrow.