Búsqueda de sitios web

typescript - Angular: al agregar o eliminar validadores y actualizar el estado del formulario usando CustomValidator... obteniendo RangeError: tamaño máximo de pila excedido


Tengo requisitos como que si un campo tiene valor, hacer que otros campos sean opcionales/eliminar obligatorio. He intentado un ejemplo como el siguiente usando un validador personalizado, pero si no uso updateValueAndValidity el estado del formulario no se actualiza y muestra invalido, y si estoy usando updateValueAndValidity obtengo un error RangeError: Se superó el tamaño máximo de la pila de llamadas.

Cómo resolver esto.

aquí está el código del validador personalizado y stackblitz:

EitherMobileOrWorkPhoneRequired(
    control: any
  ): ValidationErrors | null {
    const mobile:any = control.get('mobile');
    const workPhone:any = control.get('workPhone');
  
    const isNumberEmpty = (formGroup: AbstractControl): boolean => {
      const number = (<any>formGroup.get('number')).value;
      return number === undefined || number === null || number === '';
    };
  
    if (isNumberEmpty(mobile) && isNumberEmpty(workPhone)) {
      mobile.get('number').setValidators(Validators.required);
      

      workPhone.get('number').setValidators(Validators.required);
      

      return { required: true };
    } else if (isNumberEmpty(mobile)) {
      mobile.get('number').clearValidators();
     
    } else if (isNumberEmpty(workPhone)) {
      workPhone.get('number').clearValidators();
     
    }
      control.updateValueAndValidity();
    return null;
  }

Respuesta #1

El error RangeError: Maximum call stack size exceeded se produce porque estás llamando a la función updateValueAndValidity() dentro del propio validador.

En Angular, se recomienda evitar gestionar (eliminar/agregar) los validadores desde la propia función validadora y, en su lugar, puedes hacer eso fuera de la función validadora, y mantener la función únicamente para verificar la validez del valor del control y devolver ValidationErrors o null.

En su caso, para manejar el validador de controles cruzados en FormGroup, puede hacer lo siguiente:

  • Elimine el validador Validators.required aplicado a cada control.
  • Utiliza *ngIf="this.rootForm.errors?.required" para comprobar si el formulario es inválido en la plantilla del componente, en lugar de verificar el error de cada control.
  • Cambia el validador personalizado para que sea como el siguiente:
eitherMobileOrWorkPhoneRequired(control: any): ValidationErrors | null {
  const mobile: any = control.value.mobile?.number;
  const workPhone: any = control.value.workPhone?.number;

  if (isNumberEmpty(mobile) && isNumberEmpty(workPhone)) {
    return { required: true };
  }
  return null;
}

Y aquí hay una versión funcional de tu StackBlitz: https://stackblitz.com/edit/base-angular-12-app-jucjpk

Respuesta #2

Tienes un bucle continuo porque estás llamando a updateValueAndValidity() mientras el validador se está ejecutando.

Este método:

Recalcula el valor y el estado de validación del control.

Entonces, cuando llamas a eso, la lógica EitherMobileOrWorkPhoneRequired se vuelve a ejecutar una y otra y otra vez porque estás desencadenando un recálculo de los validadores.

Elimina la llamada a updateValueAndValidity() y funcionará como se espera.

Actualización del validador:

  EitherMobileOrWorkPhoneRequired(control: any): ValidationErrors | null {
    const mobile: any = control.get('mobile');
    const workPhone: any = control.get('workPhone');

    const isNumberEmpty = (formGroup: AbstractControl): boolean => {
      const number = (<any>formGroup.get('number')).value;
      return number === undefined || number === null || number === '';
    };

    if (isNumberEmpty(mobile) && isNumberEmpty(workPhone)) {
      return { required: true };
    }
    return null;
  }
}

El formulario ahora usa un único mensaje de error para el grupo, en lugar de uno por cada control:

<b style="color:red;font-size:20px;"
  >1.Initial both required <br />
  2.When I enter mobile number , the work phone number becomes optional and vice
  versa
  <br />
  3.Form Status should be valid on submit
</b>
<hr />
<form [formGroup]="rootForm" (ngSubmit)="onSubmit(rootForm.value)">
  <div *ngIf="!this.rootForm.valid" class="alert">
    <div style="color:red;" *ngIf="!this.rootForm.valid">
      A mobile or work phone number must be provided.
    </div>
  </div>
  <div formGroupName="mobile">
    <label>Mobile number</label>
    <input type="text" formControlName="number" />
  </div>
  <div formGroupName="workPhone">
    <label>WorkPhone number</label>
    <input type="text" formControlName="number" />
  </div>
  <button type="submit">submit</button>
</form>