Chrome autocomplete lock inputs like they are not clickable

Viewed 2177

I have a problem and very strange behavior with autofill in Chrome. When I login and then logout from app, input fields (email, password) were autocompleted but fields looks like they are frozen and not clickable.

This bug is not every time reproduced, it happens in 1/10 cases. I noticed on logout when fields get autocompleted, after 1 seconds font gets smaller in inputs and after that if you click on input there it seems like you are not clicking , nothing happens, but if you type some text (numbers not work, stays like frozen) input field goes as normal.

Here is gif with strange behavior: https://gifyu.com/image/kTkX

I tried to set autocomplete="off" , but not working.

Also I matched all css clases in input fields to see is there some overriding css, but everything looks good.

<form [formGroup]="loginForm">

<input id="emailHeader" type="text" formControlName="email" placeholder="E-mail">

<input #password type="password" formControlName="password" placeholder="Lozinka">
<input type="submit" (click)="executeLogin()"  value="Prijava">

</form> 

I expect to fields not been frozen after autofill.

public loginForm: FormGroup;
  public emailInput: ElementRef;
  public passwordInput: ElementRef;
  @ViewChild('email') set emailContent(content: ElementRef) {
    this.emailInput = content;
  }
  @ViewChild('password') set passwordContent(content: ElementRef) {
    this.passwordInput = content;
  }

  // UI helpers
  public showLoginForm: boolean;
  public showBalance: boolean;
  public player: PlayerModel = new PlayerModel({});
  public balanceInfo: BalanceInfoModel = new BalanceInfoModel({});
  public nxcsBalanceInfo: NXCSBalanceInfoModel = new NXCSBalanceInfoModel({});
  public dialogType = DialogType;
  public customMessageError = '';

  // Store
  private headerState$: Observable<any>;
  private loginState$: Observable<any>;
  private playerState$: Observable<any>;
  private emailInput$: Observable<any>;
  private passwordInput$: Observable<any>;
  private balanceState$: Observable<any>;
  private headerSubscription: Subscription;
  private loginSubscription: Subscription;
  private playerSubscription: Subscription;
  private emailSubscription: Subscription;
  private passwordSubscription: Subscription;
  private balanceSubscription: Subscription;
  // tslint:disable-next-line:no-inferrable-types
  private leftMenu: string = '';
  // tslint:disable-next-line:no-inferrable-types
  private rightMenu: string = '';

  constructor(
    private authService: AuthService,
    private fb: FormBuilder,
    private store: Store<any>,
    private route: Router,
    private localStorageService: LocalStorageService,
    private playerService: PlayerService,
    private notificationService: NotificationService,
    private dialogService: DialogService,
    private helpers: HelpersService,
    private translateCode: TranslateCode,
        private promotionService: PromotionService,
    ) {
    this.loginForm = this.buildLoginForm();
  }

  ngOnInit() {
    this.setupStore();
  }

  ngAfterViewInit() {
    this.formEventsAfterViewInit();
  }

  ngOnDestroy() {
    this.headerSubscription.unsubscribe();
    this.loginSubscription.unsubscribe();
    this.playerSubscription.unsubscribe();
    this.notificationService.closeConnection();
  }

  public executeLogin() {
    if(!this.loginForm.valid) {
      this.customMessageError = this.translateCode.transform("EMPTY_INPUT_MESSAGE");
      return;
    }

    this.authService.login(new LoginModel({...this.loginForm.value, details: this.helpers.sendSessionData()}))
      .subscribe(
        data => {
          this.localStorageService.setUserAfterLogin(data.token);
          this.customMessageError = '';
          this.loginForm.reset();
          this.route.navigate(['/app/casino']);
        },
        error => {
          error.message.includes('Račun je zaključan') ? this.store.dispatch(new PopupNotification(error.message)) : this.customMessageError = error.message
          this.addAfterErrorSubscription();
        }
      );
  }

  public openDialog(dialogType: string): void {
    switch (dialogType) {
      case DialogType.PAYMENT:
         this.openWithdrawalDialog()
        break;
      case DialogType.PAYMENT_DEPOSIT:
          this.checkRegistrationStep();
      break;
      case DialogType.TRANSACTION_HISTORY:
        this.store.dispatch(new OpenDialog({
          type: dialogType,
        }));
      break;
    }
  }

  public openInternalTransactionsDialog(): void {
    this.promotionService.getPromotionsByLocation('NXCS_DEPOSIT')
      .subscribe(
                data => this.dialogService.openDialog(MENU_DIALOGS.INTERNAL_TRANSACTION, { promotions: data }),
        error => this.dialogService.openDialog(MENU_DIALOGS.INTERNAL_TRANSACTION, { promotions: []}),
      );
  }

  public backToRegistrationStep() : void {
    switch (this.player.registrationStep) {
      case 1 :  this.route.navigate(['/auth/registration/step-two']);
                break;
      case 2 : this.route.navigate(['/auth/registration/step-three']);
                break;
      case 3 : this.route.navigate(['/auth/registration/step-four']);
                break;
      case 4 : this.route.navigate(['/auth/registration/step-five']);
                break;
      case 5 : this.route.navigate(['/auth/registration/step-six']);
                break;
      default : this.route.navigate(['/login']);
                break;
    } 
  }

  public toggleMenu(dialog): void {
    if (dialog === 'left') {
      this.leftMenu = this.leftMenu === dialog ? '' : dialog;
    }
    if (dialog === 'right') {
      this.rightMenu = this.rightMenu === dialog ? '' : dialog;
    }
    this.dispatchShadow();
  }

  private openWithdrawalDialog(_data: any = {}): void {
    const playerRole = this.localStorageService.getPlayer()['profileRole'];

    if (playerRole  === 'WITHDRAWAL_DISABLED' && this.player.uploadedAdditionalInfo) {
      this.store.dispatch(new OpenNotification({ type: NotificationType.WITHDRAWAL_DISABLED }));
      return;
    }

    playerRole  === 'WITHDRAWAL_DISABLED' ?
    this.store.dispatch(new OpenNotification({type: NotificationType.MONEY_LAUNDERING})) :
    this.dialogService.openDialog(MENU_DIALOGS.WHITDRAWALS, _data);
  }

  private openProceedToRegistration(): void {
    this.store.dispatch(new OpenNotification ({type: NotificationType.PROCEED_REGISTRATION}))
  }

  private checkRegistrationStep(): void {
    if(this.player.registrationStep < 6) {
      this.openProceedToRegistration();
    } else {
      this.dialogService.openDialog(MENU_DIALOGS.DEPOSITS, {});
    }
  }

  private dispatchShadow(): void {
    if (this.leftMenu !== '') {
      this.store.dispatch(new OpenedLeftMenu());
      this.leftMenu = '';
    }
    if (this.rightMenu !== '') {
      this.store.dispatch(new OpenedRightMenu());
      this.rightMenu = '';
    }
  }

  private buildLoginForm(): FormGroup {
    return this.fb.group({
      email: [
        '', Validators.compose([Validators.required, Validators.min(5)]),
      ],
      password: [
        '', Validators.compose([Validators.required, Validators.min(5)])
      ],
    });
  }

  private loadBalance(): void {
    this.playerService.getPlayerBalance().toPromise()
      .then(data => this.store.dispatch(new SetPlayerBalance({balanceInfo: new BalanceInfoModel(data) })))
      .then(() => {
        if (this.player.externalId) {
          this.playerService.getNXCSPlayerBalance()
            .subscribe(
              data => this.store.dispatch(new SetPlayerNXCSBalance({ nxcsBalanceInfo: new NXCSBalanceInfoModel(data) })),
              error => console.log(error),
            );
        }
      });
  }

  // Store methods
  private setupStore(): void {
    this.headerState$ = this.store.pipe(select('headerStore'));
    this.loginState$ = this.store.pipe(select('loginStore'));
    this.playerState$ = this.store.pipe(select('playerStore'));
    this.balanceState$ = this.store.pipe(select('balanceStore'));
    this.addSubscriptions();
  }

  private formEventsAfterViewInit(): void {
    if (this.emailInput && this.passwordInput) {
      this.emailInput$ = fromEvent(this.emailInput.nativeElement, 'focus');
      this.passwordInput$ = fromEvent(this.passwordInput.nativeElement, 'focus');
      this.addFormEventsSubscriptions();
    }
  }

  private addFormEventsSubscriptions(): void {
      this.emailSubscription = this.emailInput$.subscribe(() => this.triggerEmailFocus());
      this.passwordSubscription = this.passwordInput$.subscribe(() => this.triggerPasswordFocus());
  }

  private triggerEmailFocus(): void {
    this.emailInput.nativeElement.select();
    if (this.emailSubscription) {
      this.emailSubscription.unsubscribe();
    }
  }

  private triggerPasswordFocus(): void {
    this.passwordInput.nativeElement.select();
    if (this.passwordSubscription) {
      this.passwordSubscription.unsubscribe();
    }
  }

  private addSubscriptions(): void {
    this.addHeaderSubscription();
    this.addLoginSubscription();
    this.addPlayerSubscription();
    this.setBalanceSubscription();
  }

  private addHeaderSubscription(): void {
    this.headerSubscription = this.headerState$
    .subscribe(headerState => this.showLoginForm = headerState !== HeaderActionTypes.LoginPage);
  }

  private addLoginSubscription(): void {
    this.loginSubscription = this.loginState$
    .subscribe(loginState => {
      if (loginState) {
        this.loadBalance();
        this.notificationService.connect(localStorage.getItem('token'));
      } else {
        this.notificationService.closeConnection();
      }
      this.showBalance = loginState;
      this.formEventsAfterViewInit();
    });
  }

  private addPlayerSubscription(): void {
    this.playerSubscription = this.playerState$
    .subscribe(playerData => this.player = playerData);
  }

  private addAfterErrorSubscription(): void {
    this.passwordSubscription = this.passwordInput$.subscribe(() => {
      if (this.customMessageError !== '') {
        this.customMessageError = '';
        this.passwordSubscription.unsubscribe();
      }
    });
  }

}
3 Answers

This is an issue that happens every time Google Chrome autofills the fields when you permit it to save your login credentials on any form. It bugged me out the first times as well. I tried giving it a shot or two back when I encountered it for the first, but I gradually moved on and accepted it as a not-a-big deal kinda bug that every chrome user gotta have to deal with. So in conclusion, it's got nothing to do with your code. If you want to unfreeze the input fields, start typing something in them to change their value. I hope, this helps you and your nerves in accepting this browser-related behavior.

I was facing the same issue in Angular and found a workaround for this scenario, resetting the input field's value on input click seems to fix the locking issue for me.

HTML:

<input (click)="unfreezeInput($event)">

TS:

unfreezeInput(el) {
    // setting input value again makes it editable
    el.target.value = el.target.value;
}

Have you tried using the a href tag? I know it's an obvious thing, but sometimes we forget to do things like that. Also, you can use the a:hover tag in your CSS.

Related