LoginSignup
12
10

More than 5 years have passed since last update.

Vueでダイアログを動的に生成してマウントするサンプル作ってみた

Last updated at Posted at 2019-02-18

課題

Vueでサービスを作っててダイアログやモーダルを表示したいときに、公式だとdataにフラグを持たせてClickイベントでtrue/falseを切り替えるようにしていた。ただ、これだと画面内で表示したいダイアログが増えるほどにdataのフラグも増えるし、テンプレートの中に各フラグと紐づいたdialogタグを量産することになって、めちゃくちゃ見にくいし何より格好悪い。
なので、showDialog()的な感じでメソッド呼び出しでダイアログを表示させたい

やったこと

renoinn/vue-dialog-sample
というわけで、DialogHelper.showDialog()とすることでダイアログを表示させるサンプルを作ってみた。

解説

キモになるのは以下の点

Dialog.vue
methods: {
    attach () {
        if (!this.$parent) {
            this.$mount();
            document.body.appendChild(this.$el);
        } else {
            this.$mount();
            this.$parent.$el.appendChild(this.$el);
        }
    },
    remove () {
        if (!this.$parent) {
            document.body.removeChild(this.$el);
            this.$destroy();
        } else {
            this.$parent.$el.removeChild(this.$el);
            this.$destroy();
        }
    },
    close () {
        this.isShow = false;
    },
    show () {
        this.attach();
        this.isShow = true;
    },
    afterLeave () {
        this.remove();
    }
}

まずDialog.vue側で自身をappendChildしたり、removeChildするメソッドを用意してやる。この時単にdocument.body.appendChild(this.$el);とすると、Vueの仮想DOMツリーから外れてしまって、例えばvue-routerを使ってて画面遷移してもダイアログが消えないとか問題が起きてしまう。
なので、this.$parentを見て、設定されていればその下にappendChildするようにする。

DialogHelper.js
import Vue from 'vue';
import Dialog from '@/components/Dialog.vue';

const DialogHelper = {
    showDialog (context, { subject, message, ok, cancel }) {
        let DialogVM = Vue.extend(Dialog);
        let vm = new DialogVM({
            parent: context,
            propsData: {
                subject: subject,
                message: message,
                onPrimary () {
                    ok();
                    vm.close();
                },
                onSecondary () {
                    cancel();
                    vm.close();
                }
            }
        });
        vm.show();
    }
}
export default DialogHelper;

次にダイアログを生成するHelperを用意する。Vueのコンポーネントは単一コンポーネントファイルであっても、Vue.extend(Component);とすることで、ソースコード上でクラスとして使用できるようになる。
メソッドの引数でcontextを受け取って、コンストラクタでparent: contextとしてやることで、上述のthis.$parentを設定できるようにしておく。

<script>
import DialogHelper from '@/DialogHelper';

export default {
  methods: {
    showDialog () {
      DialogHelper.showDialog(this, {
        subject: 'Subject',
        message: 'open temporary dialog sample',
        ok: () => { console.log('click ok') },
        cancel: () => { console.log('click cancel') }
      });
    }
  }
}
</script>

呼び出しはHelperをimportして、showDialogから呼び出す。第一引数でthisを渡しているので、呼び出したコンポーネントの下にDialogコンポーネントがappendされることになる。
thisじゃなくて、this.$rootthis.$parentを渡すこともできる。

今回はダイアログだったけど、同じような感じでモーダルやトーストも実装できる。

参考URL

https://qiita.com/hako1912/items/8c0462203987f2cd15b1
https://kitak.hatenablog.jp/entry/2017/04/04/044829
https://github.com/paliari/v-slim-dialog

12
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
10