Ответ 1
Проблема в браузере Chrome: он не разрешает доступ к значению поля пароля после автозаполнения (прежде чем пользователь нажимает на страницу). Итак, есть два способа:
- удалить проверку поля пароля;
- отключить автозаполнение паролей.
Я внедряю страницу входа с реактивными формами Angular. Кнопка "login" отключена, если форма недействительна.
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'signin',
templateUrl: './signin.component.html'
})
export class SignInComponent implements OnInit {
private signInForm: FormGroup;
constructor(private formBuilder: FormBuilder) { }
ngOnInit() {
this.buildForm();
}
private buildForm(): void {
this.signInForm = this.formBuilder.group({
userName: ['', [Validators.required, Validators.maxLength(50)]],
password: ['', [Validators.required, Validators.maxLength(50)]]
});
this.signInForm.valueChanges
.subscribe((data: any) => this.onValueChanged(data));
this.onValueChanged();
}
private onValueChanged(data?: any) {
console.log(data);
}
Итак, когда я запускаю его в браузере, у меня есть предварительно заполненные поля "имя пользователя" и "пароли". И в консоли у меня есть значения '{имя_пользователя: "[email protected]", пароль: ""}', и в результате кнопка "login" отключена. Но если я нажимаю где-то на странице, он запускает onValueChanged, и я вижу '{имя_пользователя: "[email protected]", пароль: "123456" }', а кнопка "login" включена.
Если я иду в режиме инкогнито. У меня нет предварительно заполненных полей (они пусты), но когда я заполняю (выбираю) значения, то в консоли я вижу '{имя_пользователя: "[email protected]", пароль: "123456" }' и кнопку "login" включен без дополнительного клика.
Может быть, это разные события? Автозаполнение и автозаполнение? И Angular работает с ними по-другому?
Каков наилучший способ его решения? И почему функция onValueChanged выполняется только один раз, когда поля автозаполнения браузера?
Проблема в браузере Chrome: он не разрешает доступ к значению поля пароля после автозаполнения (прежде чем пользователь нажимает на страницу). Итак, есть два способа:
У меня возникла такая же проблема с Chrome v58.0.3029.96. Вот мое обходное решение для сохранения валидации и автозаполнения:
export class SigninComponent extends UIComponentBase
{
//--------------------------------------------------------------------------------------------
// CONSTRUCTOR
//--------------------------------------------------------------------------------------------
constructor(viewModel: SigninViewModel)
{
super(viewModel);
// Autofill password Chrome bug workaround
if (navigator.userAgent.toLowerCase().indexOf('chrome') > -1)
{
this._autofillChrome = true;
this.vm.FormGroup.valueChanges.subscribe(
(data) =>
{
if (this._autofillChrome && data.uname)
{
this._password = " ";
this._autofillChrome = false;
}
});
}
}
//--------------------------------------------------------------------------------------------
// PROPERTIES
//--------------------------------------------------------------------------------------------
private _password: string;
private _autofillChrome: boolean;
//--------------------------------------------------------------------------------------------
// COMMAND HANDLERS
//--------------------------------------------------------------------------------------------
private onFocusInput()
{
this._autofillChrome = false;
}
}
И в моем html:
<input mdInput
[placeholder]="'USERNAME_LABEL' | translate"
[formControl]="vm.FormGroup.controls['uname']"
(focus)="onFocusInput()" />
[...]
<input mdInput
type="password"
[placeholder]="'PASSWORD_LABEL' | translate"
[formControl]="vm.FormGroup.controls['password']"
(focus)="onFocusInput()"
[(ngModel)]="_password" />
Надеюсь, это поможет.
Примечание: vm
определяется в UIComponentBase
и относится к модели-образцу SigninViewModel
моего компонента. Экземпляр FormGroup
определяется в модели представления, поскольку бизнес-логика строго отделена от представления в моем приложении.
UPDATE
Так как v59, кажется, что входное фокусное событие запускается при автозаполнении полей. Таким образом, вышеупомянутое обходное решение больше не работает. Ниже приведено обновленное обходное решение с использованием тайм-аута для определения того, обновлены ли поля путем автозаполнения или пользователем (я не нашел лучшего способа):
export class SigninComponent extends UIComponentBase
{
//--------------------------------------------------------------------------------------------
// CONSTRUCTOR
//--------------------------------------------------------------------------------------------
constructor(viewModel: SigninViewModel)
{
super(viewModel);
// Autofill password Chrome bug workaround
if (navigator.userAgent.toLowerCase().indexOf('chrome') > -1)
{
this._autofillChrome = true;
setTimeout(
() =>
{
this._autofillChrome = false;
},
250 // 1/4 sec
);
this.vm.FormGroup.valueChanges.subscribe(
(data) =>
{
if (this._autofillChrome && data.uname)
{
this._password = " ";
this._autofillChrome = false;
}
});
}
}
//--------------------------------------------------------------------------------------------
// PROPERTIES
//--------------------------------------------------------------------------------------------
private _password: string;
private _autofillChrome: boolean;
}
И в моем html:
<input mdInput
[placeholder]="'USERNAME_LABEL' | translate"
[formControl]="vm.FormGroup.controls['uname']" />
[...]
<input mdInput
type="password"
[placeholder]="'PASSWORD_LABEL' | translate"
[formControl]="vm.FormGroup.controls['password']"
[(ngModel)]="_password" />
Я нашел обходной путь
Создайте некоторую переменную isDisabled: boolean = false ОБНОВЛЕНИЕ: 1.1 Еще одна вещь - var initCount: number = 0;
ngOnInit(): void {this.isDisabled = false; this.initCount++; это initCount++. } }
Создать методы
И это код:
// This method will be called async
onStart(): Observable<boolean> {
if (this.loginFomrControl.hasError('required') &&
this.passwordFormControl.hasError('required') && this.initCount == 1) {
this.isDisabled = false;
// increase init count so this method wound be called async
this.initCount++;
}
return new BehaviorSubject<boolean>(true).asObservable();
}
// This method will ve called on change
validateForm() {
if (this.loginFormControl.value != '' && this.passwordFormControl.value != '') {
this.isDisabled = false;
} else if (this.loginFomrControl.value == '' || this.passwordFormControl.value == '') {
this.isDisabled = true;
}
}
Вот образец HTML
<div class="container" *ngIf="onStart() | async">
<!-- Do Stuff -->
<mat-form-field class="login-full-width">
<input matInput placeholder="{{ 'login_email' | translate }}" id="login_email" [formControl]="loginFomrControl" (change)="validateForm()">
<mat-error *ngIf="loginFomrControl.hasError('email') && !loginFomrControl.hasError('required')">
{{'login_email_error' | translate}}
</mat-error>
<mat-error *ngIf="loginFomrControl.hasError('required')">
{{ 'login_email' | translate }}
<strong>{{ 'login_required' | translate }}</strong>
</mat-error>
</mat-form-field>
<!-- Do Stuff -->
</div>
Для полноты: у меня есть нереактивная форма входа.
<form name="loginForm" role="form" (ngSubmit)="onLoginSubmit()">
<input name="username" #usernameInp>
<input type="password" name="password" #passwordInp>
<button type="submit">Login</button>
</form>
Если я использую [(ngModel)]="username"
, переменная username
связанного компонента не заполняется, если функция автозаполнения браузера запускается.
Поэтому я вместо этого работаю с ViewChild:
@ViewChild('usernameInp') usernameInp: ElementRef;
@ViewChild('passwordInp') passwordInp: ElementRef;
...
onLoginSubmit() {
const username = this.usernameInp.nativeElement.value;
const password = this.passwordInp.nativeElement.value;
...
Таким образом, я могу получить доступ к значениям, предоставленным браузером, как только пользователь нажмет на Login.
Я обнаружил, что вы можете использовать autocomplete="new-password"
Как описано здесь
Таким образом, поля вашего пароля не будут заполняться автоматически, и это был мой случай. Есть много других атрибутов автозаполнения, доступных по ссылке выше.
это работает для меня
1. до (это автозаполнение)
<div class="form-group form-row required">
<input class="input p-l-20 form-control" formControlName='otp' type="text" name="otp" placeholder="Enter OTP">
</div>
2. после (теперь не автозаполнение, работает нормально)
<div class="form-group form-row required">
<input class="input p-l-20 form-control" formControlName='otp' type="text" name="otp" placeholder="Enter OTP">
<!-- this dummy input is solved my problem -->
<input class="hide" type="text">
</div>
примечание скрыть это класс начальной загрузки v4
Я сделал funtion
formIsValid(): boolean{
return this.form.valid;
}
и добавить к кнопке:
<button [disabled]="!formIsValid()" type="submit">LogIn</button>
Это сработало для меня