Issue
I have a complex form in my application. Part of it asks for an address, and they have to enter a physical address. There's a checkbox for marking whether or not the mailing address is the same. If it is, the mailing address fields are not required (this is the default). If they deselect the checkbox, they need to enter the mailing address. This kind of works, in that initially the mailing address is not required, but becomes required after they uncheck the box. But if they check it again, they're still required to enter the mailing address. Here's the relevant pieces of code for this:
// form declaration
const personalInfoForm = this._fb.group(
{
sameAddress: [true],
physicalAddress: this._fb.group({
line1: [
"",
[Validators.required, MhaValidators.validPhysicalStreetAddress],
],
city: ["", [Validators.required]],
state: ["", [Validators.required]],
zip: ["", [Validators.required, MhaValidators.validZipCode]],
}),
mailingAddress: this._fb.group({
line1: [""],
city: [""],
state: [""],
zip: [""],
}),
}
);
// valueChanges for same address
this.personalInfoForm
.get("sameAddress")
.valueChanges.pipe(
startWith(this.personalInfoForm.get("sameAddress").value),
tap((sameAddress: boolean) => {
if (sameAddress) {
this.personalInfoForm.controls.mailingAddress = this._fb.group({
line1: [""],
city: [""],
state: [""],
zip: [""],
});
} else {
this.personalInfoForm.controls.mailingAddress = this._fb.group({
line1: ["", [Validators.required]],
city: ["", [Validators.required]],
state: ["", [Validators.required]],
zip: ["", [Validators.required, MhaValidators.validZipCode]],
});
}
this.personalInfoForm.controls.mailingAddress.updateValueAndValidity();
this.personalInfoForm.updateValueAndValidity();
}),
untilDestroyed(this)
)
.subscribe();
// I also tried this for updating the validators:
this.personalInfoForm
.get("sameAddress")
.valueChanges.pipe(
startWith(this.personalInfoForm.get("sameAddress").value),
tap((sameAddress: boolean) => {
const mailingAddress: FormGroup = this.personalInfoForm.get(
"mailingAddress"
) as FormGroup;
if (sameAddress) {
// remove validators from mailing address
mailingAddress.get("line1").clearValidators();
mailingAddress.get("city").clearValidators();
mailingAddress.get("state").clearValidators();
mailingAddress.get("zip").clearValidators();
} else {
// add validators to mailing address
mailingAddress.get("line1").addValidators([Validators.required]);
mailingAddress.get("city").addValidators([Validators.required]);
mailingAddress.get("state").addValidators([Validators.required]);
mailingAddress
.get("zip")
.addValidators([Validators.required, MhaValidators.validZipCode]);
}
this.personalInfoForm.updateValueAndValidity();
}),
untilDestroyed(this)
)
.subscribe();
Essentially, I want to add or remove the validators for fields when the sameAddress
control toggles back and forth between true and false.
Right now, the form technically works if they don't change the same address control. If they do deselect it, they need to fill out the mailing address, even if they re select the checkbox.
I'm not sure why the validity is not updating properly. Any ideas why?
Solution
Below is a basic working example, I think once you set the validators, you need to run the updateValueAndValidity
of that particular form control, after that the code runs fine!
import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import {
FormBuilder,
FormGroup,
ReactiveFormsModule,
Validators,
} from '@angular/forms';
import { bootstrapApplication } from '@angular/platform-browser';
import { tap } from 'rxjs';
import { startWith } from 'rxjs/operators';
import 'zone.js';
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, ReactiveFormsModule],
template: `
<form [formGroup]="personalInfoForm" (ngSubmit)="onSubmit()">
<input formControlName="sameAddress" type="checkbox"/>
<fieldset formGroupName="physicalAddress">
physicalAddress | line1 :
<input formControlName="line1" type="text"/>
</fieldset>
<fieldset formGroupName="mailingAddress">
mailingAddress | line1 :
<input formControlName="line1" type="text"/>
</fieldset>
<button type="submit">submit</button>
</form>
`,
})
export class App {
name = 'Angular';
personalInfoForm: FormGroup = new FormGroup({});
constructor(private _fb: FormBuilder) {}
onSubmit() {
if (this.personalInfoForm.invalid) {
alert('invalid');
}
console.log('submit');
}
ngOnInit(): void {
const personalInfoForm = this._fb.group({
sameAddress: [true],
physicalAddress: this._fb.group({
line1: ['', [Validators.required]],
}),
mailingAddress: this._fb.group({
line1: [''],
}),
});
this.personalInfoForm = personalInfoForm;
// I also tried this for updating the validators:
this.personalInfoForm!.get('sameAddress')!
.valueChanges.pipe(
startWith(this.personalInfoForm.get('sameAddress')!.value),
tap((sameAddress: boolean) => {
console.log(sameAddress);
const mailingAddress: FormGroup = this.personalInfoForm.get(
'mailingAddress'
) as FormGroup;
const mailAddressLine1Ctrl = mailingAddress.get('line1');
if (sameAddress) {
// remove validators from mailing address
mailAddressLine1Ctrl!.clearValidators();
} else {
// add validators to mailing address
mailAddressLine1Ctrl!.addValidators([Validators.required]);
}
mailAddressLine1Ctrl!.updateValueAndValidity(); // <-- fix here
})
)
.subscribe();
}
}
bootstrapApplication(App);
Answered By - Naren Murali
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.