Search code examples
angular

Angular form doesn't validate if you don't leave the field before clicking


I have an angular form that has validations on it. The form works great if you click outside the boxes before trying to submit the form. Otherwise, if you hit enter, or click the submit button prior to having left all the boxes, it does nothing the first time because it doesn't see the form as valid yet. On the next attempt, it submits though.

I'm not sure what would cure the problem. The register method checks to see if the form is valid, but at the time the button is clicked the validation hasn't completed yet, since the onblur event has not completed on the last box data was entered in on. Since the onblur event completes as a matter of clicking the button, maybe there is a way to either fake the onblur, or at least wait for it prior to the form submission?

Anyway, I'm not sure if there is a need for the code with this problem, but I will add what I think to be the most pertinent of the code.

Form html:

    <mat-card>
      <mat-card-title>Register</mat-card-title>
      <mat-card-content>
        <form [formGroup]="form" (ngSubmit)="register(form.value)">
          <p>
            <mat-form-field>
              <input type="text" matInput placeholder="Username" formControlName="username">
              <mat-error *ngIf="checkError('username', 'required')">Username is required</mat-error>
              <mat-error *ngIf="checkError('username', 'existingUser')">Username is already taken</mat-error>
            </mat-form-field>
          </p>
          <p>
            <mat-form-field>
              <input type="password" matInput placeholder="Password" formControlName="password">
              <mat-error *ngIf="checkError('password', 'required')">Password is required</mat-error>
            </mat-form-field>
          </p>
          <p>
            <mat-form-field>
              <input type="password" matInput placeholder="Retype Password" formControlName="re_password">
              <mat-error *ngIf="checkError('re_password', 'required')">Retype Password Required</mat-error>
              <mat-error *ngIf="checkError('re_password', 'confirmedValidator')">Passwords must match</mat-error>
            </mat-form-field>
          </p>
          <p>
            <mat-form-field>
              <input type="text" matInput placeholder="First Name" formControlName="first_name">
              <mat-error *ngIf="checkError('first_name', 'required')">First name is required</mat-error>
            </mat-form-field>
          </p>
          <p>
            <mat-form-field>
              <input type="text" matInput placeholder="Last Name" formControlName="last_name">
              <mat-error *ngIf="checkError('last_name', 'required')">Last name is required</mat-error>
            </mat-form-field>
          </p>
          <p>
            <mat-form-field>
              <input type="text" matInput placeholder="Email" formControlName="email">
              <mat-error *ngIf="checkError('email', 'required')">Email is required</mat-error>
              <mat-error *ngIf="checkError('email', 'email')">Invalid email</mat-error>
              <mat-error *ngIf="checkError('email', 'existingEmail')">User with that email already exists</mat-error>
            </mat-form-field>
          </p>
          <p *ngIf="errors" class="error">
            {{errors}}
          </p>
          <div class="button">
            <button type="submit" mat-button>Register</button>
          </div>
    
        </form>
      </mat-card-content>
    </mat-card>

Formgroup:

    this.form = this.fb.group({
      username: new FormControl('', {
        validators: [Validators.required],
        asyncValidators: [this.userValidator.existingUserValidator()],
        updateOn: 'blur',
      }),
      password: new FormControl('', [
          Validators.required,
          Validators.minLength(8),
        ],
      ),
      re_password: new FormControl('',
        Validators.required,
      ),
      first_name: new FormControl('',
        Validators.required,
      ),
      last_name: new FormControl('',
        Validators.required,
      ),
      email: new FormControl('', {
        validators: [
          Validators.required,
          Validators.email,
        ],
        asyncValidators: [this.emailValidator.existingEmailValidator()],
        updateOn: 'blur',
      }),
    }, {
      validators: MatchValidator('password', 're_password'),
    });

register method:

    register(values): void {
      if (this.form.valid) {
        this.userService.register(values);
      }
    }

EDIT: To help people with similar but unrelated issues realize that their issue may be unrelated, this problem was caused by an asynchronous validation on the form field prior to submission. I assumed it was simply because the last field used had a validator.


Solution

  • You're forcing the form to use updateOn: 'blur' which means “Dont validate the input fields until the Form loses the focus”.

    If you are not using server side (async) validations you can use the “default” strategy of validation which is updateOn:'change' so the Validator is run each time the user press a Key.

    There is a third strategy which is updateOn: 'submit': Validators of each field are not run until you hit “submit”.