Angular 12, reactive form, when selecting Yes for Adult
, user is required to enter an age, else no need.
HTML:
Are you adult?
<input type="radio" formControlName="Adult" value="1"/><label>Yes</label>
<input type="radio" formControlName="Adult" value="0"/><label>No</label>
<div *ngIf="fg.controls['Adult'].value==1">
Enter your age: <input type="text" placeholder="18-120" formControlName="Age" size="5"/>
</div>
TS:
public fg: FormGroup;
this.fg = this.fb.group
(
{
Adult: ['', [Validators.required]],
Age: ['', [Validators.required, Validators.min(18), Validators.max(120)]]
}
)
Angular is not smart enough to ignore validation because of hide/show. Formgroup fg.valid
is false
when user selects No, even though Age
is hidden. One way to handle this dynamic thing is to hardcode the logic with addValidators([...]) / clearValidators() / setValidators([...])
then updateValueAndValidity()
.
Is there a better way?
Update
What I want is Angular to skip below validation rule when user chooses Yes because Age
is hidden.
Age: ['', [Validators.required, Validators.min(18), Validators.max(120)]]
To achieve that, I now have to
- attach a
(blur)=dynamicAge()
toAdult
radio button - inside
dynamicAge()
to check and set validation rule forAge
like below.
TS
private dynamicAge()
{
if (fg.controls['Adult'].value == 0) // not Adult
{
fg.controls['Age'].clearValidators(); // so fg.valid becomes true.
fg.controls['Age'].updateValueAndValidity();
}
}
Is there a better way?
CodePudding user response:
I recently did something like this in my app:
<kendo-maskedtextbox formControlName="webrelay_input_4_alarm_name"
[disabled]="!ConfigForm.get('webrelay_input_4_alarm_used')?.value">
</kendo-maskedtextbox>
Your code:
<form [formGroup]="fg" (ngSubmit)="onSubmit(fg)">
<div *ngIf="fg.get['Adult']?.value == 1">
Enter your age:
<input type="text" placeholder="18-120" formControlName="Age" size="5" />
</div>
</form>
CodePudding user response:
You can listen to Adult
form control valueChanges
then based on your condition you can enable or disable form.
When you disable form control angular will automatically exclude validation check
get ageControl(){
return this.fg.get('Age') as FormControl;
}
this.fg.get('Adult').valueChanges.subscribe(()=>{
if(value ==1){
this.ageControl.enable()
}else{
this.ageControl. disable();
}
});
CodePudding user response:
I'd personally use RxJS to handle the logic and remove or add the control.
isAdult$ | async
takes care of subscribing and unsubscribes on component destroy. Conceptually I think the age value should be true or false rather than strings '1' or '0'. This has added the benefit of Validators.requiredTrue
doing what's needed on the age control.
import { Component} from '@angular/core';
import {
FormBuilder,
FormControl,
FormGroup,
Validators,
} from '@angular/forms';
import { map, Observable, tap } from 'rxjs';
@Component({
selector: 'my-app',
template: `
<form [formGroup]="fg">
Are you adult?
<input type="radio" formControlName="Adult" [value]="true"/><label>Yes</label>
<input type="radio" formControlName="Adult" [value]="false"/><label>No</label>
<div *ngIf="isAdult$ | async">
Enter your age: <input type="number" placeholder="18-120" formControlName="Age"/>
</div>
</form>
<br/>
<br/>
<br/>
Form - {{ fg.valid ? 'Valid': 'Invalid' }}
<pre>{{ fg.value | json }}</pre>
`
})
export class AppComponent {
fg: FormGroup = this.fb.group({
Adult: [null, [Validators.required, Validators.requiredTrue]],
});
isAdult$: Observable<boolean>;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.isAdult$ = this.fg.controls['Adult'].valueChanges.pipe(
// add age control if user check `Yes` to `Are you an adult?`
tap((value) => this.onAdultValueChangeAddOrRemoveAgeControl(value))
);
}
onAdultValueChangeAddOrRemoveAgeControl(value) {
if (value) {
// check age of adult
this.fg.addControl('Age',
new FormControl(null, [
Validators.required,
Validators.min(18),
Validators.max(120),
])
);
} else {
// a child!
this.fg.removeControl('Age');
}
}
}