Home > Software engineering >  Angular Validators.required is not going with ngIf hide/show
Angular Validators.required is not going with ngIf hide/show

Time:09-15

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() to Adult radio button
  • inside dynamicAge() to check and set validation rule for Age 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');
    }
  }
}
  • Related