課題
Vueでサービスを作っててダイアログやモーダルを表示したいときに、公式だとdataにフラグを持たせてClickイベントでtrue/falseを切り替えるようにしていた。ただ、これだと画面内で表示したいダイアログが増えるほどにdataのフラグも増えるし、テンプレートの中に各フラグと紐づいたdialogタグを量産することになって、めちゃくちゃ見にくいし何より格好悪い。
なので、showDialog()的な感じでメソッド呼び出しでダイアログを表示させたい
やったこと
renoinn/vue-dialog-sample
というわけで、DialogHelper.showDialog()とすることでダイアログを表示させるサンプルを作ってみた。
解説
キモになるのは以下の点
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するようにする。
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.$root
やthis.$parent
を渡すこともできる。
今回はダイアログだったけど、同じような感じでモーダルやトーストも実装できる。
参考URL
https://qiita.com/hako1912/items/8c0462203987f2cd15b1
https://kitak.hatenablog.jp/entry/2017/04/04/044829
https://github.com/paliari/v-slim-dialog