Vue.js で同期的なダイアログ表示を行う

25019 ワード

概要

Vue.js において awaitboolean の結果を受け取れるようなダイアログ表示をプログラムから行います。
ダイアログで OK またはキャンセルが押されるまで処理をストップします。

image1

非効率な処理が含まれます。導入の際は注意事項をご確認ください。

開発環境

  • Vue 2.6.11
  • Vuetify 2.6.0

実装

呼び出し側

HelloWorld.vue
<template>
  <v-container>
    <v-row class="text-center">
      <v-col cols="12">
        <v-btn elevation="2" @click.stop="onClick">Show Alert</v-btn>
      </v-col>
    </v-row>
  </v-container>
</template>

<script lang="ts">
import Vue from "vue";
import Component from "vue-class-component";
import { confirm } from "./AsyncDialog.vue";

@Component
export default class HelloWorld extends Vue {
  onClick(): void {
    (async () => {
      const answer = await confirm("実行します。よろしいですか?");
      console.log(`answer: ${answer}`);
    })();
  }
}
</script>

ダイアログ側

AsyncDialog.vue
<template>
  <v-dialog v-model="dialog" width="500" @click:outside="cancel">
    <v-card>
      <v-card-text class="pa-4">
        {{ message }}
      </v-card-text>
      <v-divider></v-divider>
      <v-card-actions>
        <v-spacer></v-spacer>
        <v-btn @click="ok"> OK </v-btn>
        <v-btn @click="cancel"> キャンセル </v-btn>
      </v-card-actions>
    </v-card>
  </v-dialog>
</template>

<script lang="ts">
import Vue from "vue";
import vuetify from "@/plugins/vuetify";
import Component from "vue-class-component";
import { Prop } from "vue-property-decorator";

@Component
export default class AsyncDialog extends Vue {
  @Prop({ required: true }) message!: string;
  @Prop({ required: true }) okAction!: () => void;
  @Prop({ required: true }) cancelAction!: () => void;

  dialog = false;

  ok(): void {
    this.okAction();
    this.close();
  }
  cancel(): void {
    this.cancelAction();
    this.close();
  }

  created(): void {
    this.$mount();
    document.body.appendChild(this.$el);
    this.dialog = true;
  }
  close(): void {
    this.dialog = false;
    setTimeout(() => {
      if (document.body.contains(this.$el)) document.body.removeChild(this.$el);
      this.$destroy();
    }, 200);
  }
}

export const confirm = async (message: string): Promise<boolean> => {
  return new Promise<boolean>((resolve) => {
    const VM = Vue.extend(AsyncDialog);
    new VM({
      vuetify,
      parent: this,
      propsData: {
        message,
        okAction: () => resolve(true),
        cancelAction: () => resolve(false),
      },
    });
  }).catch((err) => {
    throw err;
  });
};
</script>

注意事項

ダイアログ表示の度にインスタンスを生成しているため、処理としては非効率です。
本来であれば各ページのコンポーネント内にダイアログを定義することになると思います。
あくまでも、何らかの理由でそれができない場合の対応策の一つとしてお考えください。

参考