Angular 9 - Form not valid onInit if input autofilled by Browser

Viewed 1649

So I noticed a lot of threads about this, but all older and there wasn't an official solution. Maybe now there is a solution for this problem. But basically if you have a form with a username input field, when the page loads the first time, if the browser autofills that value (Because you saved your credentials in your browser), the "login" button will be still disabled because the form is still invalid until the users clicks anywhere for the first time.

The latest proposed solution I found was using Angular official: AutofillMonitor But it doesnt work:

 @ViewChild("email") inputEl: ElementRef;

constructor(
    private formBuilder: FormBuilder,
    private _autofill: AutofillMonitor
  ){
    this.emailForm = this.formBuilder.group(
      {
        userLogin: [
          "",
          {
            validators: [Validators.required],
          },
        ],
        password: [
          "",
          {}
        ]
      }
    );
  }

ngAfterViewInit() {
    this._autofill.monitor(this.inputEl)
    .subscribe(e => {
        if (e.isAutofilled) {
          // this works, the code gets in here onInit
        this.forceValidityCheck();
        }
      }
    );
  }

forceValidityCheck() {
    this.emailForm.controls["userLogin"].updateValueAndValidity();
  }

HTML CODE:

<ng-container>
      <form [formGroup]="emailForm" (ngSubmit)="checkAndLoadUserInfo(emailForm.get('userLogin').value)">
      <mat-form-field appearance="standard" class="standardInputField">
        <mat-label translate>enter_email.username</mat-label>
        <input matInput formControlName="userLogin" #email type="email" placeholder="name@example.org" (keyup.enter)="checkAndLoadUserInfo(emailForm.get('userLogin').value)" autofocus autocomplete="username">
        <label class="pswValidationLabels" *ngIf="emailForm.get('userLogin').hasError('doesNotExist')" translate>
          enter_email.unknown_user
          </label>
      </mat-form-field>

      <mat-form-field appearance="standard" class="hiddenFormField">
      <input matInput type="password" autocomplete="current-password">
      </mat-form-field>

      <div>
        <button [disabled]="!emailForm.get('userLogin').valid" mat-raised-button>
        </button>
      </div>
    </form>
    </ng-container>

But the form stays invalid even like this, until the users clicks anywhere on the screen, then it gets valid.

Is there an official solution for this?

1 Answers

I do see some approvements that may help, because I don't experience your issue.

First, who do you bind the disabled property of the submit button to the validity of the username field only and not the entire form?

Also, I think you ngSubmit is can be a little bit hard to maintain this way, because again, you explicitly use the username field value to pass, while the entire form is available for you in the TS file. So the checkAndLoadUserInfo() should not require a parameter.

Then I believe the form build is somewhat 'the old' way to create reactive forms, have you seen that you can create a form control? It works the same way:

this.newGameForm = new FormGroup({
    userLogin: new FormControl('', [
        Validators.required
    ])
});

Now what I always do, is create a function called initForm() or something similar and call that from the ngOnInit(). Once that is done, you have an instance of your form. You can now easily manipulate form values by setting them like so:

this.formName.patchValue({ userLogin: 'new value'});

In case you should already be able to get the value from somewhere at the time you init the form, you could also use the first parameter to create the form with the initial value in the desired field. Both ways will trigger validation and update the form's state.

Related