Cordova 8 / IoS 12 / Angular 6
I have a cordova application with a login form. Everything is working well apart from this.
On ios, the user is prompted by apple to enter a saved password when he focuses in the form. When the user uses this ios autofill feature, the form doest not detect the new values and the data stays empty. (but the user can see the credentials appearing on the screen)
Here is my login form :
<form (ngSubmit)="login()" [formGroup]="loginForm">
<input type="email" formControlName="username" name="username"/>
<input type="password" formControlName="password" name="password"/>
<button class="transparent-button primary">Login</button>
</form>
And in my component :
constructor(
/* ... */
) {
this.loginForm = formBuilder.group({
username: ["", Validators.required],
password: ["", Validators.required]
});
};
login(){
//this.loginForm.value.username and
this.loginForm.value.password stay empty when user
autofill
}
I also tried with template driven forms : same problem. Apple is now blocking the publishing of my app because of this. Is this a known problem of ios/cordova ?
Thanks
FWIW, I was having this same issue, see my workaround below.
I'm using reactive forms, and was experiencing the issue on a login form where I have just a username and password input. I implemented a workaround where I subscribe to changes on the form and then detect if the native password value is equal the password form field at the time of the change (technically 500ms after the change to wait for the autofill to input the password value). If the two are different I manually override the form value with the native value which triggers validation of the form.
Hope this helps someone!
// login-form.component.ts
import { Component, ViewChild, ElementRef } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
@Component({
selector: 'app-login-form',
template: require('./login-form.component.pug')(),
styleUrls: ['./login-form.component.scss'],
})
export class LoginFormComponent {
protected loginForm: FormGroup;
@ViewChild('password') public passwordRef: ElementRef;
constructor(private _formBuilder: FormBuilder) {
this.loginForm = this.getForm();
// NOTE: this hack triggers form validation on iOS autofill
this.loginForm.valueChanges
// wait for 500ms to be sure the autofill is finished with the password field
.debounceTime(500)
.subscribe((changes) => {
// manually fetch the value from nativeElement
let passVal = this.passwordRef.nativeElement.value; //(<HTMLInputElement>document.querySelector('[name="password"]')).value;
// if the value saved in the form is different from the input, manually update it
if (passVal !== changes.password) {
this.loginForm.get('password').setValue(passVal);
}
})
;
// NOTE: this hack triggers form validation on iOS autofill
}
protected getForm(): FormGroup {
return this._formBuilder.group({
username: ['', [
Validators.required,
Validators.email,
]],
password: ['', [
Validators.required,
]],
});
}
public onSubmit(login: FormGroup) {
// ...
}
}
// login-form.component.pug
// NOTE: pug + bootstrap 4
h1 Login
form([formGroup]="loginForm", (ngSubmit)="onSubmit(loginForm)")
.form-group
input.form-control(
formControlName="username",
name="username",
type="email",
placeholder="Username",
autocomplete="on",
autocorrect="off",
autocapitalize="off",
spellcheck="false",
)
.form-group
input.form-control(
#password,
formControlName="password",
name="password",
type="password",
placeholder="Password",
autocomplete="on",
autocorrect="off",
autocapitalize="off",
spellcheck="false",
)
input.mb-3.btn.btn-lg(
type="submit",
value="Login",
[class.btn-outline-primary]="loginForm.valid",
[class.btn-outline-secondary]="!loginForm.valid",
[disabled]="!loginForm.valid",
)