2

Let's say I have a checkout form that contains a child address component as follows

<form [formGroup]="checkoutForm">
  <input formControlName="fullName">
  /** more inputs **/
  <address-form></address-form>
  <button type="submit">Submit</button>
</form>

At the moment I have the checkoutForm built like:

this.checkoutForm = this.formBuilder.group({
    fullName: ['', Validators.required]
}); 

The addressForm template is like:

<form [formGroup]="addressForm">
   <input formControlName="street">
   <input formControlName="town"
</form>

And built like:

this.addressForm = this.formBuilder.group({
  street: ['', [Validators.required]],
  town: ['', Validators.required]
});

Now the issue I have is I dont know how to

1 - Validate the parent form only when the child form is valid.

2 - Validate the child form when the parent form is submitted.

The only way I could think about it is to have an @Output() addressResponse that will emit on this.addressForm.valueChanges with the validity and data. Something like:

this.addressForm.valueChanges.subscribe(data => {
   let form = this.addressForm.valid ?  
         { valid: true, value: data }: 
         { valid: false, value: data };
   this.addressResponse.emit(form);
});

And the parent form component can use this emitted data.

And also have an @Input() parentFormSubmitted that I can use to display the errors in the template of AddressForm

   <input formControlName="town"
   <div *ngIf="town.hasError('required') && (parentFormSubmitted || town.dirty">Town is a required field</div>

While this would work, I am not sure it is the optimal solution. I was wondering if there a more Reactive Form way of doing things. (Maybe include the AddressForm group in the definition of the CheckoutForm group?...)

1 Answer 1

3

You are completely correct with your comment:

I was wondering if there a more Reactive Form way of doing things. (Maybe include the AddressForm group in the definition of the CheckoutForm group?...)

You can build your form in your parent, and pass the nested group address to your child as an @Input. As objects in JS are mutable, your don't need an EventEmitter to pass any changes to your parent from child, changes will be caught "automatically" and you can therefore do all validation from the parent.

So in parent build your form like:

this.checkoutForm = this.formBuilder.group({
  fullName: ['', [...]],
  address: this.formBuilder.group({
    street: ['', [...]],
    town: ['', [...]]
  })
})

then in your child tag pass the address to child as @Input:

<address-form [address]="checkoutForm.controls.address"></address-form>

In your child you mark the @Input:

@Input() address: FormGroup;

and the template in child would look like:

<div [formGroup]="address">
  <input formControlName="street"><br>
  <!-- Validation messages -->
  <input formControlName="town">
  <!-- Validation messages -->
</div>

and as mentioned, you can now handle all validation from the parent, as the parent is aware of what values the child has :)

Sign up to request clarification or add additional context in comments.

5 Comments

Thank you for the answer @AJT_82. How would I do if I had two address instances that point to the same child component in my parent form. Would I have to define two separate addressForm groups in the main checkoutForm group?
You would perhaps then want a form array, where you can push those two addresses, or if this is dynamic and you can also push addresses dynamically. Something like this: stackoverflow.com/a/43521492/6294072
The idea is to have a checkbox for an address that will be present in DOM but shown only when a checkbox is checked. Do you recommend the formArray?
Well it all depends. Are you still having more than one address group? If so and the number is dynamic, use a form array. In case you know that there is only two addresses it's really up to you. If I knew I had only two groups, I'll maybe go with just having two groups instead of form array especially if you have some sort of condition on when to show these :)
Thank you sir. Your comments and directions were very helpful

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.