web-dev-qa-db-fra.com

Angular 2 et remplissage automatique du navigateur

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?

17
A. Gladkiy

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:

  1. supprimer la validation du champ mot de passe;
  2. désactiver l'auto-complétion du mot de passe.
14
Yaroslav Sivakov

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" />
2
Valone

J'ai trouvé une solution de contournement

  1. Create var isDisabled: boolean = false

    ngOnInit (): void { this.isDisabled = false; this.initCount ++; }

  2. 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;
    }
  }
  1. Mettez à jour votre HTML avec quelque chose comme ça:

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.

1
rochasdv

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

0
Hemanth SP

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 .

0
bgerth

http://webagility.com/posts/the-ultimate-list-of-hacks-for-chromes-forced-yellow-background-on-autocompleted-puts

Essayez ceci. J'ai eu un peu mal à la tête, mais ça a fait l'affaire.

input:-webkit-autofill {
  -webkit-transition-delay: 99999s;
}
0
RedSky