JavaScript
vue.js

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


課題

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