1

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.

5
  • Why are you binding [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=preview Commented Nov 30, 2016 at 21:56
  • You're correct! I apologize. I was seeing that locally because of how I was updating which form should be displayed when the dropdown was selected. I was deleting the other form components and then doing myForm.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). Commented Dec 1, 2016 at 2:18
  • IIRC disabled controls don't count towards validation. Programmatically disable via 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. Commented Dec 1, 2016 at 5:49
  • If I am remembering wrong then I'll actually go do some research. Commented Dec 1, 2016 at 5:58
  • Thanks! That did work. Feel free to add the correct answer, and I'll accept it. Commented Dec 1, 2016 at 16:04

1 Answer 1

1

Per the comments, the issue was hiding controls and them still counting towards validation.

If you're hiding and showing select FormGroups and don't want them to count towards validation or the form.value property then you can call disable() either on the individual controls or on the containing FormGroup:

this.myForm.addressInfo.disable(); // disables the entire addressInfo group
// which removes it from validation, thus preventing false flags.
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.