Error alerts in angular reactive forms appear one step late

最后都变了- 提交于 2020-01-24 12:08:12

问题


I am using Angular 8 and I am trying to create a registration form with reactive forms. I had created some alert messages to show validation errors.

However, the previous alert is shown instead of the current alert! (one step late)

app.component.ts file:

    import { Component } from '@angular/core';
    import { FormBuilder, Validators, FormControl } from '@angular/forms';

    @Component({
      selector: 'my-app',
      templateUrl: './app.component.html',
      styleUrls: [ './app.component.css' ]
    })
    export class AppComponent  {

      form = this.fBuilder.group({
        userName: ['', [
          Validators.required,
          Validators.minLength(3),
          this.userNameValidation.bind(this)
        ]]
      });

      requiredError: boolean;
      minLengthError: boolean;

      constructor(private fBuilder: FormBuilder) { }

      // User Name Validation
      userNameValidation(control: FormControl) {
        if (control.hasError('required') && control.dirty) {
          this.requiredError = true;
        } else {
          this.requiredError = false;
        }

        if (control.hasError('minlength') && control.dirty) {
          this.minLengthError = true;
        } else {
          this.minLengthError = false;
        }
      }
    }

app.component.html file:

    <form [formGroup]="form">
      <label>User Name: </label>
        <input formControlName="userName" type="text">
    </form>

    <!-- alerts -->
    <p *ngIf="requiredError">User Name is required</p>
    <p *ngIf="minLengthError">User Name must be at least 3 characters</p>

Here is a simple StackBlitz showing what I mean:

Stackblitz Example 🚀🚀

Try typing one letter, then delete it and see what happens.

Thanks in advance!


回答1:


Based on the comment on other answer, you want to "send errors to another component". If you indeed need the custom validator for that, we need to remember that angular forms are in fact asynchronous, so it takes an extra tick to form being correctly evaluated. I would never recommend to use setTimeout in asynchronous functions, but we can know that the form values have been set after waiting a tick, so it is safe to use. Therefore wrap everything inside a timeout:

setTimeout(() => {
  if (control.hasError("required") && control.dirty) {
    this.requiredError = true;
  } else {
    this.requiredError = false;
  }

  if (control.hasError("minlength") && control.dirty) {
    this.minLengthError = true;
  } else {
    this.minLengthError = false;
  }
});

STACKBLITZ

But if you have a child component like <hello></hello> in your template and you want to send the errors to that child via @Input, you don't need to use the above approach, you can also just send the boolean value of the validity to child, like...

<hello *ngIf="form.get('userName').dirty" [requiredError]="form.hasError('required', 'userName')"></hello>

and catch that in your child with for example:

@Input() requiredError: boolean;

ngOnChanges() {
  if (this.requiredError) {
    alert('field required!')
  }
}

STACKBLITZ




回答2:


You don't need a custom validator to display error in your forms. What you are doing with this.userNameValidation.bind(this) is creating a custom validator.

What you can do is create a variable in your component (.ts file) to reference the form control in your form group.

In this instance, get userName() { return this.form.get('userName'); }.

This creates a variable userName for your template to "get". Subsequently, you can use this variable to check for errors like the following:

template .html file:

<form [formGroup]="form">
  <label for="userName">User Name: </label>

  <input type="text" formControlName="userName" name="userName">
  <br>
  <ng-container *ngIf="userName.errors?.required && (userName?.touched || userName?.dirty)">
    <small class="formError">User Name is required!</small>
  </ng-container>
  <ng-container *ngIf="userName.errors?.minlength && (userName?.touched || userName?.dirty)">
    <small class="formError">User Name must be at least 3 characters!</small>
  </ng-container>
  <br>
</form>

As you can see, userName.errors?.minlength and userName.errors?.required will directly reference the userName in your component file. You also want to check for .touched and .dirty because you don't want to display the error only until your users have interacted with the form control.

For future reference, have a read on Angular official docs on reactive form validation (Note: read the first part only, the subsequent section in on template-driven forms)

You should also use <ng-container> because *ngIf will render the elements in your DOM even if the *ngIf evaluates to false. See this Angular: The ng-container Element

component .ts file

import { Component } from '@angular/core';
import { FormBuilder, Validators, FormControl, FormGroup } from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  // requiredError: boolean; //not required
  // minLengthError: boolean; //not required

  form: FormGroup;

  constructor(private fBuilder: FormBuilder) { }

  get userName() { return this.form.get('userName'); } //create a userName variable for template to reference

  ngOnInit(){
    this.form = this.fBuilder.group({ 
      userName: [
        '', [ Validators.required, Validators.minLength(3)]
      ]
    });
  }

  ngAfterViewChecked(){
    console.log(this.userName.errors)
  }

Forked Stackblitz Example ⚡⚡

Hope this helped!



来源:https://stackoverflow.com/questions/58550692/error-alerts-in-angular-reactive-forms-appear-one-step-late

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!