🙌

[Angular] モバイルSafariでもinputでの自動入力を検知する

2022/07/24に公開

https://twitter.com/PentaPROgram/status/1550787703230574592

というツイートを見つけて、そういえば記事化してなかったので対応方法をまとめておきます。モバイルSafari(WKWebView)の不具合なのですが、まだ修正行われていないんですよね。ちなみにCordova/Capacitorか、もしくは自作のWebViewアプリをつくってない限り、この不具合は関係ありません。

https://bugs.webkit.org/show_bug.cgi?id=226023

以下の説明はion-inputでしていますが、Issueをみる感じ、通常のinputでも再現するようです。

なぜ発生するのか

autocompleteでの入力を検知してもInputイベントを発生させてくれないからです。

どうやって直すか

Changeイベントは発生するので、Changeイベントを検知して値を渡します。

<ion-input #emailInput [(ngModel)]="email" type="email" name="email"></ion-input>

という要素があるとして、これをautocompleteに対応させましょう。基本的なAngularのComponentをベースに説明します。

  @Component({
    selector: 'app-home',
    templateUrl: 'home.page.html',
    styleUrls: ['home.page.scss'],
  })
  export class HomePage {
+   public email = '';
+   @ViewChild('emailInput', { static: true }) emailInput: IonInput;
  }

まず要素の値を email というプロパティでバインディングします。また 要素に #emailInput という名前をつけてるので、 ViewChild を使ってこの要素を、 emailInput というプロパティで参照します。続いて、この要素の change イベントを監視します。

+ async ngOnInit() {
+   const nativeEmailInput = await this.emailInput.getInputElement();
+   nativeEmailInput.addEventListener('change', (ev: Event) => {
+     requestAnimationFrame(() => {
+       this.email = (ev.target as HTMLInputElement).value;
+     });
+   });
+ }

ion-input は、ラップされているinput要素を getInputElement メソッドで取得することができるので、ライフサイクル ngOnInit が実行された時に this.emailInput.getInputElement() でinput要素を取得します。そして、 addEventListenerchange イベントを監視し、検知した値を email プロパティにいれるようにします。

ただ問題はこの対応って「ユーザが通常入力してもchangeイベントが走り続けて、値が更新される」ことなんですよね。なので、requestAnimationFrame を使って、通常のバインディング後に change イベントの値が入るようにしています。ただあまりスマートではないので、例えば「ユーザが入力しかけたあとにautocompleteを使うシチューションは除外する」と割り切るなら

async ngOnInit() {
  const nativeEmailInput = await this.emailInput.getInputElement();

  nativeEmailInput.addEventListener('change', (ev: Event) => {
    if (this.email.length === 0) {
      this.email = (ev.target as HTMLInputElement).value;
    };
  });
}

というように、 email プロパティが空の場合にしか値が入らないようにするとか、「最初のchangeイベントだけでいい」なら、すぐremoveListenerしてしまうという方法もあります。

対応するIssue: https://github.com/ionic-team/ionic-framework/issues/23335

ブラウザの不具合対応なので少し不格好になってしまいますが、参考になれば幸いです。
それではまた。

Discussion