Vue.js で自作した確認ダイアログで、ユーザーの返答を Promise を使って待つ


自分用の備忘録的に…

まず、完成したのはこんなのです。よくあるTODOリストだけど、いちいち確認してくるモーダルのダイアログが追加時と完了設定時に入っています。(メンディ)

素直に実装するなら、TODO追加ボタンのonclickにダイアログの表示処理、ダイアログの「はい」ボタンのonclickにTODO追加の処理を書いていくことになると思います。でもそれだと同じダイアログを別の動作の中で使いまわすのも辛そうですし、後からコードを見た時に一連の動作が追いづらくなりますよね

そこでこんな実装にしてみました

<template>
  <div>
    <form @submit.prevent="doAdd">
      <input type="text" v-model="title">
      <button>add</button>
    </form>
    <ul>
      <li v-for="(todo, i) in todos" :key="i">
        <span class="todo-title" :class="{'done': todo.done}">{{todo.title}}</span>
        <button @click="doMarkAsDone(i)" v-if="!todo.done">done</button>
      </li>
    </ul>
    <div class="modal" v-if="isShowConfirmModal">
      <div class="overlay"></div>
      <div class="modal-container">
        <div class="modal-body">
          <p>マ?</p>
          <button @click="$emit('answerdConfirm', true)">はい</button>
          <button @click="$emit('answerdConfirm', false)">いいえ</button>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'app',
  data() {
    return {
      title: '',
      todos: [],
      isShowConfirmModal: false,
    };
  },
  methods: {
    async doAdd() {
      if (!await this.askConfirm()) return
      this.todos.unshift({
        title: this.title,
        done: false,
      })
      this.title = ''
    },
    async doMarkAsDone(todoIndex) {
      if (!await this.askConfirm()) return
      this.todos = this.todos.map((todo, index) => ({
        ...todo,
        done: index === todoIndex || todo.done,
      }))
    },
    askConfirm() {
      this.isShowConfirmModal = true
      return new Promise(resolve =>
        this.$once('answerdConfirm', confirmValue => {
          this.isShowConfirmModal = false
          resolve(confirmValue)
        })
      )
    },
  },
}
</script>

<style>
...
</style>

いろいろ荒いけど…

ポイントは、確認モーダル表示メソッド( askConfirm() )でモーダルを開きつつ、 Promise を返しているところです。
ユーザーが「はい」か「いいえ」のどちらかをクリックすると、 $emit でイベントが発行され、それを $once が受け取ると、先ほど返した Promise が解決されます。
このようにすることで、TODO追加メソッド( doAdd() )、TODO完了設定メソッド( doMarkAsDone )のどちらでも、処理の一連の流れを分断することなく、ユーザーがダイアログで入力した値を受け取ることができます。

以上!