Search code examples
angulartypescriptangular-validation

How do I conditionally validate input fields in my reactive form?


I have a form where a user can register thesmselves or a guest. If a user is registering themselves, I don't need to validate guest info input fields, and I do need to do it if a guest is being registered by a user. This is the link to stackblitz.

Here is a simplified example of my reactive form with validation that is not working correctly:

export class AppComponent {
  name = 'Angular ' + VERSION.major;

  guestInfoValidators = [];

  profileForm: FormGroup;

  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    this.profileForm = this.fb.group({
      registrant: ['currentUser', Validators.required],
      guestInfo: this.fb.group({
        firstName: ['', this.guestInfoValidators],
        lastName: ['', ],
        badgeId: ['', ],
      }),
    });

    this.profileForm.get('registrant').valueChanges.subscribe((value) => {
      console.log(value);
      if (value === 'guest') {
        this.profileForm
          .get('guestInfo.firstName')
          .setValidators(this.guestInfoValidators.concat(Validators.required));
      } else {
        this.profileForm
          .get('guestInfo.firstName')
          .setValidators(this.guestInfoValidators.concat());
      }
    });
  }
}

Here is HTML:

<form [formGroup]="profileForm">
  <div>
    <input type="radio" 
    value="currentUser" 
    formControlName='registrant' 
    value='currentUser'
    id="currentUser"
    />
    <label for="currentUser">Register Myself</label>
  </div>
  <div>
    <input type="radio"  
    formControlName='registrant' 
    value="guest" 
    id="guest"
    />
    <label for="guest">Register Guest</label>
  </div>
  
  <div *ngIf="profileForm.get('registrant')?.value=='guest'">
  <div formGroupName = 'guestInfo' class="guestData">
  <label for="firstName">First Name</label>
   <input id="firstName" type="text" formControlName="firstName">

   <label for="lastName">Last Name</label>
   <input id="lastName" type="text" formControlName="lastName">

   <label for="badgeId">Badge</label>
   <input type="text" id="badgeId" formControlName="badgeId">
 </div>
  </div>
</form>

In my console I get this error:

ERROR Error: NG0100: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'VALID'. Current value: 'INVALID'.. Find more at https://angular.io/errors/NG0100
    at throwErrorIfNoChangesMode (errors.ts:63:9)
    at bindingUpdated (bindings.ts:72:12)
    at interpolation1 (interpolation.ts:74:21)
    at Object.ɵɵtextInterpolate1 (text_interpolation.ts:93:24)
    at AppComponent_Template (app.component.html:41:4)
    at executeTemplate (shared.ts:528:1)
    at refreshView (shared.ts:381:62)
    at refreshComponent (shared.ts:1732:1)
    at refreshChildComponents (shared.ts:129:3)
    at refreshView (shared.ts:435:6)

I'm very new to Angular, and I'd appreciate if you could tell me what I'm doing wrong.


Solution

  • ngOnInit() {
        this.profileForm = this.fb.group({
          registrant: ['currentUser', Validators.required],
          guestInfo: this.fb.group(
            {
              firstName: [''],
              lastName: [''],
              badgeId: [''],
            },
          ),
        });
    
        this.profileForm.get('registrant').valueChanges.subscribe((value) => {
          console.log(value);
          debugger;
          if (value === 'guest') {
            this.profileForm
              .get('guestInfo.firstName')
              .setValidators([Validators.required]);
            this.profileForm.get('guestInfo.firstName').updateValueAndValidity();
          } else {
            this.profileForm.get('guestInfo.firstName').clearValidators();
            this.profileForm.get('guestInfo.firstName').updateValueAndValidity();
          }
        });
    }