Bootstrap の Modal を promisify して async/await で直列に書けるようにする

js から Bootstrap の Modal を表示してそのモーダル上での操作に応じてなにかしたいとき(典型的には確認ダイアログとか)、Modal を Promise として扱えるようにしておくと async/await で直列に書けて便利です。

const promisifyModal = (modal, param) =>{
    const $modal = $(modal);
    return new Promise((resolve) => {
        $modal
            .data('modal-param', param)
            .data('modal-result', null)
            .off('hidden.bs.modal')
            .one('hidden.bs.modal', () => {
                resolve($modal.data('modal-result'));
                $modal
                    .data('modal-param', null)
                    .data('modal-result', null)
            })
            .modal('show')
    });
};

モーダルを開く側は次のように await でモーダルが閉じるのを待ってその結果を得ることが出来ます。

$('#show-modal').on('click', async (ev) => {
    console.log('モーダルを表示します');
    try {
        // モーダルにわたすパラメータ
        const param = 'ほげほげ';
        // モーダルを表示して閉じるまで待つ
        const result = await promisifyModal('#modal', param);
        if (result != null) {
            // モーダルの結果でなにかする
            console.log(`モーダルの結果は ${result} です`);
        }
    } finally {
        // モーダルが閉じた後
        console.log('モーダルが閉じました');
    }
});

表示されるモーダルの側は次のような HTML と js です。モーダルの表示時は $modal.data('modal-param') でモーダルを開く側から渡されたパラメータが受け取れて、モーダルの結果は $modal.data('modal-result') に入れています。

<div class="modal fade" id="modal" tabindex="-1">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title">モーダル</h5>
        <button type="button" class="close" data-dismiss="modal">
          <span>&times;</span>
        </button>
      </div>
      <div class="modal-body">
        <input type="text">
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-primary" data-modal-ok>OK</button>
        <button type="button" class="btn btn-secondary" data-dismiss="modal">キャンセル</button>
      </div>
    </div>
  </div>
</div>
const $modal = $('#modal');

$modal.on('show.bs.modal', (ev) => {
    // モーダルに渡されたパラメータでなにかする
    const param = $modal.data('modal-param');
    $modal.find('input[type="text"]').val(param);
});

$modal.find('[data-modal-ok]').on('click', (ev) => {
    // モーダルの結果を設定して閉じる
    const result = $modal.find('input[type="text"]').val();
    $modal.data('modal-result', result);
    $modal.modal('hide');
});