¿Cómo puedo configurar manualmente un campo de formulario angular como no válido?

190

Estoy trabajando en un formulario de inicio de sesión y si el usuario ingresa credenciales no válidas, queremos marcar los campos de correo electrónico y contraseña como no válidos y mostrar un mensaje que dice que el inicio de sesión falló. ¿Cómo hago para configurar estos campos como no válidos desde una devolución de llamada observable?

Modelo:

<form #loginForm="ngForm" (ngSubmit)="login(loginForm)" id="loginForm">
  <div class="login-content" fxLayout="column" fxLayoutAlign="start stretch">
    <md-input-container>
      <input mdInput placeholder="Email" type="email" name="email" required [(ngModel)]="email">
    </md-input-container>
    <md-input-container>
      <input mdInput placeholder="Password" type="password" name="password" required [(ngModel)]="password">
    </md-input-container>
    <p class='error' *ngIf='loginFailed'>The email address or password is invalid.</p>
    <div class="extra-options" fxLayout="row" fxLayoutAlign="space-between center">
     <md-checkbox class="remember-me">Remember Me</md-checkbox>
      <a class="forgot-password" routerLink='/forgot-password'>Forgot Password?</a>
    </div>
    <button class="login-button" md-raised-button [disabled]="!loginForm.valid">SIGN IN</button>
     <p class="note">Don't have an account?<br/> <a [routerLink]="['/register']">Click here to create one</a></p>
   </div>
 </form>

Método de inicio de sesión:

 @ViewChild('loginForm') loginForm: HTMLFormElement;

 private login(formData: any): void {
    this.authService.login(formData).subscribe(res => {
      alert(`Congrats, you have logged in. We don't have anywhere to send you right now though, but congrats regardless!`);
    }, error => {
      this.loginFailed = true; // This displays the error message, I don't really like this, but that's another issue.
      this.loginForm.controls.email.invalid = true;
      this.loginForm.controls.password.invalid = true; 
    });
  }

Además de establecer el indicador inválido de entradas en verdadero, he intentado establecer el email.validindicador en falso y establecer también loginForm.invaliden verdadero. Ninguno de estos hace que las entradas muestren su estado no válido.

efarley
fuente
¿Está su backend en un puerto diferente al angular? Si es así, esto podría ser un problema de CORS. ¿Qué marco estás utilizando para el backend?
Mike3355
Puedes usar el setErrosmétodo. Consejos: debe agregar el validador requerido en su archivo componente. ¿También hay una razón específica para usar ngModel con formas reactivas?
developer033
@ developer033 un poco tarde para la fiesta aquí, pero esos no se parecen a Reactive Form, sino a una plantilla basada en una plantilla.
thenetimp

Respuestas:

261

en componente:

formData.form.controls['email'].setErrors({'incorrect': true});

y en HTML:

<input mdInput placeholder="Email" type="email" name="email" required [(ngModel)]="email"  #email="ngModel">
<div *ngIf="!email.valid">{{email.errors| json}}</div>
Julia Passynkova
fuente
13
¿Y cómo eliminas el error después? setErrors({'incorrect': false})o setErrrors({})no están trabajando para mí
Robouste
3
¿Puedo configurar un formulario reactivo completo como válido o no válido en lugar de restablecer los campos?
xtremist
29
@Robouste puede eliminar los errores manualmentesetErrrors(null)
Idrees Khan
66
Además de esta respuesta: este código no funciona para mí sin formData.form.controls['email'].markAsTouched();@ M.Farahmand mencionado a continuación. Usando setErrors({'incorrect': true})solo establece la ng-invalidclase css para la entrada. Espero que esto ayude a alguien.
Barabas
44
¿Y qué pasa si hay más validadores, como "requerido"? ¿SetErrors (null) elimina ese error?
Please_Dont_Bully_Me_SO_Lords
88

Agregando a la respuesta de Julia Passynkova

Para configurar el error de validación en el componente:

formData.form.controls['email'].setErrors({'incorrect': true});

Para desarmar el error de validación en el componente:

formData.form.controls['email'].setErrors(null);

Tenga cuidado al desarmar los errores, nullya que esto sobrescribirá todos los errores. Si desea conservar algunos, es posible que primero deba verificar la existencia de otros errores:

if (isIncorrectOnlyError){
   formData.form.controls['email'].setErrors(null);
}
Eric D.
fuente
3
¿Es posible desactivar el error de validación usando algo como formData.form.controls ['email']. SetErrors ({'incorrecto': falso});
rudrasiva86 23/0618
1
¿Qué pasa con las formas reactivas?
seidme
26

Estaba tratando de llamar setErrors()dentro de un controlador ngModelChange en forma de plantilla. No funcionó hasta que esperé una marca con setTimeout():

modelo:

<input type="password" [(ngModel)]="user.password" class="form-control" 
 id="password" name="password" required (ngModelChange)="checkPasswords()">

<input type="password" [(ngModel)]="pwConfirm" class="form-control"
 id="pwConfirm" name="pwConfirm" required (ngModelChange)="checkPasswords()"
 #pwConfirmModel="ngModel">

<div [hidden]="pwConfirmModel.valid || pwConfirmModel.pristine" class="alert-danger">
   Passwords do not match
</div>

componente:

@ViewChild('pwConfirmModel') pwConfirmModel: NgModel;

checkPasswords() {
  if (this.pwConfirm.length >= this.user.password.length &&
      this.pwConfirm !== this.user.password) {
    console.log('passwords do not match');
    // setErrors() must be called after change detection runs
    setTimeout(() => this.pwConfirmModel.control.setErrors({'nomatch': true}) );
  } else {
    // to clear the error, we don't have to wait
    this.pwConfirmModel.control.setErrors(null);
  }
}

Las trampas como esta me están haciendo preferir formas reactivas.

Mark Rajcok
fuente
Cannot find name 'NgModel'.error para la línea @ViewChild('pwConfirmModel') pwConfirmModel: NgModel;cualquier solución para este problema
Deep 3015
¿Qué pasa con tener que usar setTimeOuts? He notado esto y parece que los controles no se actualizan de inmediato. Esto introduce una gran cantidad de código hacky para evitar esta limitación.
Jake Shakesworth
Gracias. Lo sabía setErrorspero no funcionó hasta que lo usésetTimeout
Sampgun
25

En la nueva versión del material 2, cuyo nombre de control comienza con el prefijo mat setErrors () no funciona, en cambio, la respuesta de Juila se puede cambiar a:

formData.form.controls['email'].markAsTouched();
M_Farahmand
fuente
1

Aquí hay un ejemplo que funciona:

MatchPassword(AC: FormControl) {
  let dataForm = AC.parent;
  if(!dataForm) return null;

  var newPasswordRepeat = dataForm.get('newPasswordRepeat');
  let password = dataForm.get('newPassword').value;
  let confirmPassword = newPasswordRepeat.value;

  if(password != confirmPassword) {
    /* for newPasswordRepeat from current field "newPassword" */
    dataForm.controls["newPasswordRepeat"].setErrors( {MatchPassword: true} );
    if( newPasswordRepeat == AC ) {
      /* for current field "newPasswordRepeat" */
      return {newPasswordRepeat: {MatchPassword: true} };
    }
  } else {
    dataForm.controls["newPasswordRepeat"].setErrors( null );
  }
  return null;
}

createForm() {
  this.dataForm = this.fb.group({
    password: [ "", Validators.required ],
    newPassword: [ "", [ Validators.required, Validators.minLength(6), this.MatchPassword] ],
    newPasswordRepeat: [ "", [Validators.required, this.MatchPassword] ]
  });
}
Romano Ninguno
fuente
Esto puede ser "hacky", pero me gusta porque no tiene que configurar un ErrorStateMatcher personalizado para que funcione con errores de entrada de material angular.
David Melin
1

En mi forma Reactiva, necesitaba marcar un campo como no válido si se marcaba otro campo. En ng versión 7 hice lo siguiente:

    const checkboxField = this.form.get('<name of field>');
    const dropDownField = this.form.get('<name of field>');

    this.checkboxField$ = checkboxField.valueChanges
        .subscribe((checked: boolean) => {
            if(checked) {
                dropDownField.setValidators(Validators.required);
                dropDownField.setErrors({ required: true });
                dropDownField.markAsDirty();
            } else {
                dropDownField.clearValidators();
                dropDownField.markAsPristine();
            }
        });

Por lo tanto, cuando marque la casilla, establece el menú desplegable según sea necesario y lo marca como sucio. Si no lo marca como tal, no será inválido (por error) hasta que intente enviar el formulario o interactuar con él.

Si la casilla de verificación está configurada como falsa (sin marcar), borramos el validador requerido en el menú desplegable y lo restablecemos a un estado prístino.

Además, ¡recuerde darse de baja de los cambios de campo de monitoreo!

Katana24
fuente
1

También puede cambiar el 'tipo' viewChild a NgForm como en:

@ViewChild('loginForm') loginForm: NgForm;

Y luego haga referencia a sus controles de la misma manera que @Julia mencionó:

 private login(formData: any): void {
    this.authService.login(formData).subscribe(res => {
      alert(`Congrats, you have logged in. We don't have anywhere to send you right now though, but congrats regardless!`);
    }, error => {
      this.loginFailed = true; // This displays the error message, I don't really like this, but that's another issue.

      this.loginForm.controls['email'].setErrors({ 'incorrect': true});
      this.loginForm.controls['password'].setErrors({ 'incorrect': true});
    });
  }

Establecer los errores en nulo borrará los errores en la interfaz de usuario:

this.loginForm.controls['email'].setErrors(null);
Jacques
fuente
0

Aunque su solución tardía pero siguiente funcionó de mí.

    let control = this.registerForm.controls['controlName'];
    control.setErrors({backend: {someProp: "Invalid Data"}});
    let message = control.errors['backend'].someProp;
Dila Gurung
fuente
-9

Para prueba unitaria:

spyOn(component.form, 'valid').and.returnValue(true);
Vang Bờm
fuente