web-dev-qa-db-ja.com

Angular 2およびブラウザの自動入力

ログインページをAngular反応型フォームで実装しています。フォームが無効な場合、ボタン「ログイン」は無効になります。

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);
    }

そのため、ブラウザで起動すると、「userName」フィールドと「passwords」フィールドが事前に入力されています。そして、コンソールには値 '{userName: "[email protected]"、パスワード: ""}'があり、結果としてボタン "login"は無効になっています。しかし、ページのどこかをクリックするとonValueChangedがトリガーされ、 '{userName: "[email protected]"、password: "123456"}'、およびボタン「ログイン」が有効になります。

シークレットモードにした場合。事前に入力されたフィールドはありません(空です)が、値を入力(選択)すると、コンソールに '{userName: "[email protected]"、password: "123456が表示されます"} '、ボタン「ログイン」は追加のクリックなしで有効になります。

異なるイベントかもしれませんか?オートフィルとオートコンプリート?そしてangularはそれらとは異なる動作をしますか?

それを解決する最良の方法は何ですか?そして、なぜonValueChanged関数はブラウザがフィールドを自動入力するときに一度だけ実行されるのですか?

17
A. Gladkiy

Chromeブラウザの問題:自動入力後(ユーザーがページをクリックする前)のパスワードフィールドの値へのアクセスを許可しません。したがって、2つの方法があります。

  1. パスワードフィールドの検証を削除します。
  2. パスワードの自動補完を無効にします。
15

autocomplete="new-password"を使用できることがわかりました

記載されているとおり here

このようにして、パスワードフィールドはまったく自動入力されず、それが私の場合でした。上記のリンクで利用可能な他の多くのオートコンプリート属性があります。

4
rochasdv

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

役に立てば幸いです。

vmUIComponentBaseで定義され、コンポーネントのビューモデル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" />
2
Valone

回避策を見つけました

  1. Var isDisabledを作成します:boolean = false UPDATE:1.1もう1つ-var initCount:number = 0;

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

  2. メソッドを作成する

そして、これはコードです:

// 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. 次のようにhtmlを更新します。

これが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>

これは私のために働いています

1。before(itの自動入力)

<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。after(現在は自動充填ではなく、正常に動作しています)

<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>

hide is bootstrap v4 class

1
giveJob

完全を期すために:私は非反応的なログインフォームを持っています。

_<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"を使用する場合、ブラウザautofill機能が起動しても、関連するコンポーネントの変数usernameは読み込まれません。

したがって、代わりにViewChildを使用します。

_@ViewChild('usernameInp') usernameInp: ElementRef;
@ViewChild('passwordInp') passwordInp: ElementRef;
...
onLoginSubmit() {
  const username = this.usernameInp.nativeElement.value;
  const password = this.passwordInp.nativeElement.value;
  ...
_

このようにして、ユーザーがLoginをクリックすると、ブラウザーが提供する値にアクセスできます。

1
bgerth

私のソリューション Gist Hub

// <ion-input (change)="fixAutoFill($event, 'password')" #passwordInput autocomplete="off" formControlName="password" type="password"></ion-input>
// <ion-input (change)="fixAutoFill($event, 'username')" #usernameInput autocomplete="off" type="text" formControlName="username"></ion-input>


  fixAutoFill(event, type) {
    const value = event.target.value;
    if (type === 'username') {
      this.loginForm.patchValue({
        username: value
      }, {emitEvent: true, onlySelf: false});
    } else {
      this.loginForm.patchValue({
        password: value
      }, {emitEvent: true, onlySelf: false});
    }
    // console.log('after click', this.loginForm.value);
  }

  @ViewChild('usernameInput') usernameInput: IonInput;
  @ViewChild('passwordInput') passwordInput: IonInput;
0

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

これを試してください。少し頭を悩ませましたが、これでうまくいきました。

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