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?...)

callback
  • 3,981
  • 1
  • 31
  • 55

1 Answers1

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 :)

AT82
  • 71,416
  • 24
  • 140
  • 167
  • 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? – callback Oct 02 '17 at 09:25
  • 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: https://stackoverflow.com/a/43521492/6294072 – AT82 Oct 02 '17 at 09:36
  • 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? – callback Oct 02 '17 at 09:51
  • 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 :) – AT82 Oct 02 '17 at 09:59
  • Thank you sir. Your comments and directions were very helpful – callback Oct 02 '17 at 13:56