J'implémente une page de connexion avec des formes réactives angulaires. Le bouton "login" est désactivé si le formulaire est invalide.
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);
}
Ainsi, lorsque je le lance dans un navigateur, j'ai des champs "nom d'utilisateur" et "mots de passe" préremplis. Et dans la console, j'ai les valeurs '{userName: "[email protected]", le mot de passe: ""} "et le bouton de résultat" login "est désactivé. Mais si je clique quelque part sur la page, cela déclenche onValueChanged et je vois '{nomutilisateur: "[email protected]", mot de passe: "123456"}, et le bouton "connexion" est activé.
Si je vais en mode incognito. Je n'ai pas de champs préremplis (ils sont vides), mais lorsque je renseigne (sélectionne) des valeurs, je vois dans la console '{nom d'utilisateur: "[email protected]", mot de passe: "123456"}, et Le bouton "connexion" est activé sans aucun clic supplémentaire.
Peut-être sont-ils des événements différents? Autofill et autocomplete? Et angulaire fonctionne avec eux différemment?
Quelle est la meilleure façon de le résoudre? Et pourquoi onValueChanged la fonction est exécutée une seule fois lorsque les champs de remplissage automatique du navigateur sont activés?
Le problème dans le navigateur Chrome: il ne permet pas d'accéder à la valeur du champ de mot de passe après le remplissage automatique (avant que l'utilisateur clique sur la page). Donc, il y a deux façons:
J'ai rencontré le même problème avec Chrome v58.0.3029.96. Voici ma solution de contournement pour conserver la validation et l'auto-complétion:
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;
}
}
Et dans mon 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" />
J'espère que ça aide.
Remarque : vm
est défini dans UIComponentBase
et fait référence au modèle de vue SigninViewModel
de mon composant. L'instance FormGroup
est définie dans le modèle de vue, car la logique métier est strictement séparée de la vue dans mon application.
METTRE &AGRAVE; JOUR
Depuis la v59, il semble que l'événement de focus de l'entrée est déclenché lors de la saisie automatique des champs. La solution ci-dessus ne fonctionne donc plus. Voici la solution de contournement mise à jour, utilisant un délai d'expiration pour déterminer si les champs sont mis à jour par complétement automatique ou par l'utilisateur (je n'ai pas trouvé de meilleur moyen):
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;
}
Et dans mon 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" />
J'ai trouvé une solution de contournement
Create var isDisabled: boolean = false
ngOnInit (): void { this.isDisabled = false; this.initCount ++; }
Créer des méthodes
Et voici le code:
// 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;
}
}
Voici un exemple du code 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>
J'ai découvert que vous pouvez utiliser autocomplete="new-password"
Comme il est décrit ici
De cette façon, vos champs de mot de passe ne seront pas du tout remplis automatiquement, et c’était mon cas . Il existe de nombreux autres attributs de complétion automatique disponibles dans le lien ci-dessus.
cela a fonctionné pour moi
1. avant (le remplissage automatique)
<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. après (maintenant, pas de remplissage automatique, fonctionne bien)
<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>
note hide est une classe de bootstrap v4
Par souci d’exhaustivité, j’ai un formulaire de connexion non réactif.
<form name="loginForm" role="form" (ngSubmit)="onLoginSubmit()">
<input name="username" #usernameInp>
<input type="password" name="password" #passwordInp>
<button type="submit">Login</button>
</form>
Si j'utilise [(ngModel)]="username"
, la variable username
du composant associé n'est pas renseignée si la fonctionnalité de navigateur remplissage automatique est activée.
Par conséquent, je travaille plutôt avec ViewChild:
@ViewChild('usernameInp') usernameInp: ElementRef;
@ViewChild('passwordInp') passwordInp: ElementRef;
...
onLoginSubmit() {
const username = this.usernameInp.nativeElement.value;
const password = this.passwordInp.nativeElement.value;
...
De cette façon, je peux accéder aux valeurs fournies par le navigateur une fois que l'utilisateur a cliqué sur Login .
Essayez ceci. J'ai eu un peu mal à la tête, mais ça a fait l'affaire.
input:-webkit-autofill {
-webkit-transition-delay: 99999s;
}