😸

IonicのAlertControllerでPromiseを使ったスマートなリファクタリング方法

2020/12/10に公開

この記事はIonic Framework / Capacitor Advent Calendar 2020の記事です。


本記事はIonic/Angularでコードを書いていますが、Ionic/React、Ionic/Vueでも同様のコードになります。

AlertControllerを利用すれば、シンプルにユーザに選択肢を提示し、アクションを得ることができます。例えば以下のような感じです。

これを実現してるコードはたったこれだけです。

let result;
const alert = await this.alertCtrl.create({
  header: 'Ionic Frameworkのリンクを開きます',
  message: 'ウェブサイト https://ionicframework.jp/docs/ に移動します。',
  buttons: [
    {
      text: 'キャンセル',
      role: 'cancel',
      handler: () => result = false,
    },
    {
      text: '開く',
      handler: () => result = true,
    },
  ],
});
await alert.present();
await alert.onWillDismiss(); // アラートが閉じるまでPromiseが解決しない
console.log(result); // ユーザの選択がわかる

ユーザが「キャンセル」を選択した場合は、キャンセル側の(配列[0]の) handler が実行されます。「開く」も同様です。ですので、上記のように onWillDismiss でユーザがアラートを閉じるのを待つようにすれば、変数 result で結果を得ることができます。

けれど、実行メソッドに、AlertControllerの表示とその処理を書くと冗長になりがちなので、リファクタリングする時にはこれを別メソッドにしたくなります。

動かないリファクタリング例 ☒

本来なら、以下のようなコードでユーザのアクションを受け取ることができれば、リファクタリングはとても楽になります。しかし以下は動作しません。

public async alertHandle() {
  const result = await this.alert();  // ユーザの選択が返ってこない
  console.log(result); // undefined
}

private async alert(): Promise<boolean> {
  const alert = await this.alertCtrl.create({
    header: 'Ionic Frameworkのリンクを開きます',
    message: 'ウェブサイト https://ionicframework.jp/docs/ に移動します。',
    buttons: [
      {
        text: 'キャンセル',
        role: 'cancel',
        handler: () => false,
      },
      {
        text: '開く',
        handler: () => true,
      },
    ],
  });
  await alert.present();
  return await alert.onWillDismiss<boolean>();
}

handlerreturn しないので、上記のコードで結果を得ることはできません。

動くリファクタリング例 ☑

そこで、これをPromiseにラップして、ユーザのアクションを取得しましょう。


public async alertHandle() {
  const result = await this.alert();  // ユーザの選択が返ってくる
  console.log(result); // true || false
}

private async alert(): Promise<boolean> {
  let resolveFunction: (confirm: boolean) => void;
  const promise = new Promise<boolean>((resolve) => (resolveFunction = resolve));
  const alert = await this.alertCtrl.create({
    header: 'Ionic Frameworkのリンクを開きます',
    message: 'ウェブサイト https://ionicframework.jp/docs/ に移動します。',
    buttons: [
      {
        text: 'キャンセル',
        role: 'cancel',
        handler: () => resolveFunction(false),
      },
      {
        text: '開く',
        handler: () => resolveFunction(true),
      },
    ],
  });
  await alert.present();
  return promise;
}

resolveFunctionpromise を解決して値を返すため、こうすると結果を得ることができます。

これで、AlertControllerを処理するメソッドと利用するメソッドを分けることができました。個人的にはAlertControllerをPageで処理するのは見通しが悪くなりがちなので、返り値の型だけつけてServiceに移行するのはおすすめです。ぜひご活用ください。

それではまた。

Discussion