Comment puis-je définir manuellement un champ de formulaire angulaire comme invalide?

190

Je travaille sur un formulaire de connexion et si l'utilisateur entre des informations d'identification non valides, nous voulons marquer les champs e-mail et mot de passe comme non valides et afficher un message indiquant que la connexion a échoué. Comment puis-je définir ces champs pour qu'ils ne soient pas valides à partir d'un rappel observable?

Modèle:

<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éthode de connexion:

 @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; 
    });
  }

En plus de définir l'indicateur non valide des entrées sur true, j'ai essayé de définir l' email.validindicateur sur false et de définir également le paramètre loginForm.invalidsur true. Aucun de ces éléments ne provoque l'affichage de l'état invalide des entrées.

efarley
la source
Votre backend est-il sur un port différent de celui angulaire? Si tel est le cas, cela pourrait être un problème CORS. Quel framework utilisez-vous pour le backend.
Mike3355
Vous pouvez utiliser la setErrosméthode. Conseils: Vous devez ajouter le validateur requis sur votre fichier de composant. Existe-t-il également une raison spécifique d'utiliser ngModel avec des formes réactives?
developer033
@ developer033 un peu en retard à la fête ici, mais ceux-ci ne ressemblent pas à un formulaire réactif, mais à un formulaire basé sur un modèle.
thenetimp

Réponses:

261

dans le composant:

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

et 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
la source
13
Et comment supprimez-vous l'erreur par la suite? setErrors({'incorrect': false})ou setErrrors({})ne travaille pas pour moi
Robouste
3
Puis-je définir un formulaire réactif complet comme valide ou non valide au lieu de réinitialiser les champs?
xtremist
29
@Robouste vous pouvez supprimer les erreurs manuellement parsetErrrors(null)
Idrees Khan
6
En plus de cette réponse: ce code ne fonctionne pas pour moi sans formData.form.controls['email'].markAsTouched();comme @ M.Farahmand mentionné ci-dessous. En utilisant setErrors({'incorrect': true})juste la ng-invalidclasse css définie pour l'entrée. J'espère que cela aide quelqu'un.
Barabas le
4
Et s'il y a plus de validateurs, comme "requis" - setErrors (null) supprime-t-il cette erreur?
Please_Dont_Bully_Me_SO_Lords
88

Ajout à la réponse de Julia Passynkova

Pour définir l'erreur de validation dans le composant:

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

Pour annuler l'erreur de validation dans le composant:

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

Soyez prudent en désactivant les erreurs en utilisant nullcar cela écrasera toutes les erreurs. Si vous souhaitez en conserver, vous devrez peut-être d'abord vérifier l'existence d'autres erreurs:

if (isIncorrectOnlyError){
   formData.form.controls['email'].setErrors(null);
}
Éric D.
la source
3
Est-il possible d'annuler l'erreur de validation en utilisant quelque chose comme formData.form.controls ['email']. SetErrors ({'incorrect': false});
rudrasiva86
1
Qu'en est-il des formes réactives?
seidme
26

J'essayais d'appeler à l' setErrors()intérieur d'un gestionnaire ngModelChange sous forme de modèle. Cela n'a pas fonctionné jusqu'à ce que j'attende un tick avec setTimeout():

modèle:

<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>

composant:

@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);
  }
}

Des pièges comme celui-ci me font préférer les formes réactives.

Mark Rajcok
la source
Cannot find name 'NgModel'.erreur pour la ligne @ViewChild('pwConfirmModel') pwConfirmModel: NgModel;tout correctif pour ce problème
Deep 3015
Qu'est-ce qui se passe avec l'utilisation de setTimeOuts? J'ai remarqué cela et il semble que les contrôles ne se mettent pas immédiatement à jour. Cela introduit beaucoup de code hacky pour contourner cette limitation.
Jake Shakesworth
Merci. Je savais setErrorsmais cela n'a pas fonctionné jusqu'à ce que je l'utilisesetTimeout
Sampgun
25

Dans la nouvelle version du matériau 2 dont le nom de contrôle commence par le préfixe mat setErrors () ne fonctionne pas, la réponse de Juila peut être changée en:

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

Voici un exemple qui fonctionne:

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] ]
  });
}
Romain Aucun
la source
Cela peut être "hacky", mais je l'aime parce que vous n'avez pas besoin de définir un ErrorStateMatcher personnalisé pour fonctionner avec les erreurs Angular Material Input!
David Melin
1

Dans mon formulaire réactif, je devais marquer un champ comme invalide si un autre champ était coché. Dans la version ng 7, j'ai fait ce qui suit:

    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();
            }
        });

Donc ci-dessus, lorsque je coche la case, il définit la liste déroulante comme requis et le marque comme sale. Si vous ne le marquez pas comme tel, il ne sera pas invalide (par erreur) tant que vous n'aurez pas essayé de soumettre le formulaire ou d'interagir avec lui.

Si la case à cocher est définie sur false (non cochée), nous effaçons le validateur requis dans la liste déroulante et le réinitialisons à un état vierge.

N'oubliez pas non plus de vous désabonner de la surveillance des changements de champ!

Katana24
la source
1

Vous pouvez également changer le type de viewChild en NgForm comme dans:

@ViewChild('loginForm') loginForm: NgForm;

Et puis référencez vos contrôles de la même manière que @Julia l'a mentionné:

 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});
    });
  }

La définition des erreurs sur null effacera les erreurs sur l'interface utilisateur:

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

Bien que sa solution tardive mais suivante ait fonctionné pour moi.

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

Pour le test unitaire:

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