Wie kann ich ein Angular-Formularfeld manuell als ungültig festlegen?

189

Ich arbeite an einem Anmeldeformular. Wenn der Benutzer ungültige Anmeldeinformationen eingibt, möchten wir sowohl das E-Mail- als auch das Kennwortfeld als ungültig markieren und eine Meldung anzeigen, dass die Anmeldung fehlgeschlagen ist. Wie gehe ich vor, um diese Felder nach einem beobachtbaren Rückruf als ungültig festzulegen?

Vorlage:

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

Anmeldemethode:

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

Zusätzlich zum Setzen des ungültigen Eingangsflags auf true habe ich versucht, das email.validFlag auf false und das loginForm.invalidauf true zu setzen. Keine dieser Aktionen bewirkt, dass die Eingaben ihren ungültigen Status anzeigen.

Efarley
quelle
Befindet sich Ihr Backend an einem anderen Port als eckig? In diesem Fall könnte dies ein CORS-Problem sein. Welches Framework verwenden Sie für das Backend?
Mike3355
Sie können setErrosMethode verwenden. Tipps: Sie sollten den erforderlichen Validator zu Ihrer Komponentendatei hinzufügen. Gibt es auch einen bestimmten Grund, ngModel mit reaktiven Formen zu verwenden?
Entwickler033
@ developer033 etwas spät zur Party hier, aber diese sehen nicht wie Reactive Form aus, sondern wie ein Template-gesteuertes Formular.
thenetimp

Antworten:

259

in Komponente:

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

und in HTML:

<input mdInput placeholder="Email" type="email" name="email" required [(ngModel)]="email"  #email="ngModel">
<div *ngIf="!email.valid">{{email.errors| json}}</div>
Julia Passynkova
quelle
13
Und wie können Sie den Fehler anschließend beheben? setErrors({'incorrect': false})oder setErrrors({})arbeiten nicht für mich
Robouste
3
Kann ich ein ganzes reaktives Formular als gültig oder ungültig festlegen, anstatt Felder zurückzusetzen?
Xtremist
28
@ Robouste können Sie die Fehler manuell entfernen durchsetErrrors(null)
Idrees Khan
5
Zusätzlich zu dieser Antwort: Dieser Code funktioniert bei mir nicht ohne formData.form.controls['email'].markAsTouched();das unten erwähnte @ M.Farahmand. Verwenden Sie setErrors({'incorrect': true})einfach die ng-invalidCSS-Klasse für die Eingabe. Ich hoffe es hilft jemandem.
Barabas
3
Und was ist, wenn es mehr Validatoren gibt, wie "erforderlich" - löscht setErrors (null) diesen Fehler?
Please_Dont_Bully_Me_SO_Lords
86

Hinzufügen zu Julia Passynkovas Antwort

So legen Sie einen Validierungsfehler in der Komponente fest:

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

So deaktivieren Sie den Validierungsfehler in der Komponente:

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

Seien Sie vorsichtig beim Deaktivieren der Fehler mit, nullda dadurch alle Fehler überschrieben werden. Wenn Sie einige behalten möchten, müssen Sie möglicherweise zuerst prüfen, ob andere Fehler vorliegen:

if (isIncorrectOnlyError){
   formData.form.controls['email'].setErrors(null);
}
Eric D.
quelle
3
Ist es möglich, den Validierungsfehler mit formData.form.controls ['email'] zu deaktivieren? SetErrors ({'falsch': false});
Rudrasiva86
1
Was ist mit reaktiven Formen?
Seid Mehmedovic
24

Ich habe versucht, setErrors()einen ngModelChange-Handler in einem Vorlagenformular aufzurufen. Es hat nicht funktioniert, bis ich einen Tick gewartet habe mit setTimeout():

Vorlage:

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

Komponente:

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

Solche Fallstricke lassen mich reaktive Formen bevorzugen.

Mark Rajcok
quelle
Cannot find name 'NgModel'.Fehler für Zeile @ViewChild('pwConfirmModel') pwConfirmModel: NgModel;jede Korrektur für dieses Problem
Deep 3015
Was ist los mit setTimeOuts? Ich habe dies bemerkt und es scheint, dass sich die Steuerelemente nicht sofort selbst aktualisieren. Dies führt eine Menge Hacky-Code ein, um diese Einschränkung zu umgehen.
Jake Shakesworth
Vielen Dank. Ich wusste, setErrorsaber es funktionierte nicht, bis ich es benutztesetTimeout
Sampgun
24

In der neuen Version von Material 2, dessen Kontrollname mit dem Mattenpräfix setErrors () beginnt, funktioniert dies nicht. Stattdessen kann Juilas Antwort wie folgt geändert werden:

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

Hier ist ein Beispiel, das funktioniert:

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] ]
  });
}
Roman Keine
quelle
Dies mag "hacky" sein, aber ich mag es, weil Sie keinen benutzerdefinierten ErrorStateMatcher festlegen müssen, um mit Angular Material Input-Fehlern zu arbeiten!
David Melin
1

In meinem reaktiven Formular musste ich ein Feld als ungültig markieren, wenn ein anderes Feld aktiviert war. In ng Version 7 habe ich folgendes gemacht:

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

Wenn ich das Kontrollkästchen aktiviere, wird das Dropdown-Menü wie erforderlich festgelegt und als verschmutzt markiert. Wenn Sie es nicht als solches markieren, wird es (irrtümlich) nicht ungültig, bis Sie versuchen, das Formular zu senden oder mit ihm zu interagieren.

Wenn das Kontrollkästchen auf false gesetzt ist (nicht aktiviert), löschen wir den erforderlichen Validator in der Dropdown-Liste und setzen ihn auf einen makellosen Zustand zurück.

Denken Sie auch daran, sich von der Überwachung von Feldänderungen abzumelden!

Katana24
quelle
1

Sie können den viewChild-Typ auch wie folgt in NgForm ändern:

@ViewChild('loginForm') loginForm: NgForm;

Und dann verweisen Sie auf Ihre Steuerelemente auf die gleiche Weise, wie @Julia es erwähnt hat:

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

Wenn Sie die Fehler auf null setzen, werden die Fehler auf der Benutzeroberfläche behoben:

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

Obwohl seine späte, aber folgende Lösung von mir aus funktioniert hat.

    let control = this.registerForm.controls['controlName'];
    control.setErrors({backend: {someProp: "Invalid Data"}});
    let message = control.errors['backend'].someProp;
Dila Gurung
quelle
3
macht diese letzte Zeile etwas?
Tom Brito
-8

Für Unit-Test:

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