0

Have been using Angular 13 in my application and have a scenario where I have to remove a required field validation on value changes. Find this stackblitz for a demo of what I am trying to achieve.

In the demo atleast one field is required. If I add firstname, validation on nickname should be removed or vice versa.

Have tried using the updateValueandValidity method but that doesn't seem to work as well. If I do not pass any opts on the updateValueandValidity method, Maximum Call stack error appears as valuechanges is called infinitely. Below is code from stackblitz.

HTML:

   <h1>Mandatory to add atleast one name</h1>
   <form [formGroup]="registerForm">
     <div class="col-lg-3 col-md-3">
        <mat-form-field>
          <mat-label>First Name</mat-label>
          <input matInput formControlName="firstName" />
        </mat-form-field>
     </div>
     <div class="col-lg-3 col-md-3">
       <mat-form-field>
         <mat-label>Nick Name</mat-label>
         <input matInput formControlName="nickName" />
       </mat-form-field>
     </div>
  </form>

TS:

import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
  public registerForm: FormGroup;

  ngOnInit() {
    this.registerForm = new FormGroup({
       firstName: new FormControl('', Validators.required),
       nickName: new FormControl('', Validators.required),
    });
    this.toggleRequiredValidationOnInput();
  }
  toggleRequiredValidationOnInput() {
    Object.keys(this.registerForm.controls).forEach((field)=>{
     this.registerForm.get(field)?.valueChanges.subscribe(()=> {
       let isMandatory = false;
       isMandatory = !Object.keys(this.registerForm.controls).some((field) => {
       return this.registerForm.get(field)?.value;
     });
     this.updateFieldWithMandatoryCheck(isMandatory);
   });
 });
}
updateFieldWithMandatoryCheck(isMandatory) {
  Object.keys(this.registerForm.controls).forEach((field) => {
    if (isMandatory) { this.registerForm.get(field).addValidators(Validators.required);
  } else {
    this.registerForm.get(field).removeValidators(Validators.required);
  }
  this.registerForm
    .get(field)
    .updateValueAndValidity({ onlySelf: true, emitEvent: false });
  });
 }
}
2
  • It had been discussed here stackoverflow.com/questions/46488078/… Commented Sep 6, 2022 at 21:02
  • @MissaConstantPierrehardwork the issue is both fields are interdependent and changes in one emit changes in the other and back and forth which is causing stack overflow error Commented Sep 6, 2022 at 21:04

2 Answers 2

3

Instead of adding/removing the validators manually, you could put a custom cross-field-validator function onto the FormGroup, like this:

private checkAtLeastOneRequired(form: FormGroup): ValidationErrors {
    const ffFirstName: AbstractControl = form.get('firstName');
    const ffNickName: AbstractControl = form.get('nickName');

    let errors: ValidationErrors = null;
    if (!ffFirstName.value && !ffNickName.value) {
        errors = { atLeastOneRequired: true };
    }

    ffFirstName.setErrors(errors);
    ffNickName.setErrors(errors);

    return errors;
}

ngOnInit(): void {
    this.registerForm = new FormGroup({
       firstName: new FormControl(''),
       nickName: new FormControl(''),
    }, this.checkAtLeastOneRequired);
}
Sign up to request clarification or add additional context in comments.

Comments

1

Ok, here is what I did. The trick to breaking the loop was using the distinctUntilChanged pipe. See the demo here

ngOnInit() {
    this.registerForm = new FormGroup({
      firstName: new FormControl('', Validators.required),
      nickName: new FormControl('', Validators.required),
    });

    this.registerForm
      .get('firstName')
      .valueChanges.pipe(takeUntil(this.destroy$), distinctUntilChanged())
      .subscribe({
        next: (val) => {
          if (val) {
            this.registerForm.get('nickName').clearValidators();
            this.registerForm.get('nickName').updateValueAndValidity();
          } else {
            this.registerForm
              .get('nickName')
              .setValidators([Validators.required]);
            this.registerForm.get('nickName').updateValueAndValidity();
          }
        },
      });

    this.registerForm
      .get('nickName')
      .valueChanges.pipe(takeUntil(this.destroy$), distinctUntilChanged())
      .subscribe({
        next: (val) => {
          if (val) {
            this.registerForm.get('firstName').clearValidators();
            this.registerForm.get('firstName').updateValueAndValidity();
          } else {
            this.registerForm
              .get('firstName')
              .setValidators([Validators.required]);

            this.registerForm.get('firstName').updateValueAndValidity();
          }
        },
      });
  }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.