Reputation: 4122
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?...)
Upvotes: 2
Views: 3064
Reputation: 73357
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 :)
Upvotes: 3