#nexttickの使い方
Vuejsでは、ある値を変更し、nexttickをすることで、リアクティブに動く値が変更したあとの処理を実行することができます。
<template>
<div id='name'>{{name}}</div>
</template>
<script>
methods: {
setNameAndSayHello(name) {
this.name = name
console.log(document.getElementById('name').innerHTML)
}
}
</script>
setNameAndSayHello()の中で参照されているDOMはまだ描画されていないです。そのためまだ古い値が使われています。
<script>
methods: {
setNameAndSayHello(name) {
this.name = name
this.$nextTick(function () {
console.log(document.getElementById('name').innerHTML)
})
}
}
</script>
nextTickの中で実施することで、描画されたあとの値を利用することができます。
#VuetifyのVDialogでダイアログ閉じたあとの処理の実装をnextTickでやった場合
印刷確認ダイアログを実装したく、以下のようにv-dialogを使い実装しました。
<template>
<v-dialog v-model="dialog" width="500">
<template v-slot:activator="{ on, attrs }">
<v-btn v-bind="attrs" v-on="on">印刷</v-btn>
</template>
<div class="confirm">
印刷してよろしいですか?
<div>
<v-btn @click="print">はい</v-btn>
<v-btn @click="dialog=false">いいえ</v-btn>
</div>
</div>
</v-dialog>
</template>
<script>
methods: {
print(e) {
this.dialog=false
this.$nextTick(() => {
window.print()
})
}
}
</script>
しかし、ダイアログが完全に閉じない段階で印刷が動いてしまい、ダイアログも印刷されてしまいます。nextTickが効かないと感じてしまいます。
これは、ダイアログを閉じる処理がCSSのアニメーションにより、だんだん消えるようになっているため、ダイアログが残ったままになってしまうためです。
これのかんたんな対応策としてはsetTimeout()を使うことです。
<script>
methods: {
print(e) {
this.dialog=false
setTimeout(()=> {
window.print()
}, 500)
}
}
</script>
これでダイアログが閉じたときぐらいに、print()処理が呼び出されて、無事にダイアログを除いて印刷されます。
でもこれではちゃんとDOMがなくなったをしっかり捕捉できているかわからないです。
#VuetifyのCSSアニメーションの実装
VuetifyのVDialogは、VueのTransitionにより実装されています。
VueのTransitionだと、ダイアログが閉じた場合をハンドリングするには、afterLeaveを指定すれば良いのですが、それが実装されていないです。。。
そこで以下のようなクラスを作ります。
<script>
import Vue from "vue";
const VDialog = Vue.options.components["VDialog"];
export default {
name: "transitonable-dialog",
extends: VDialog,
methods: {
afterLeave(e) {
console.log("in afterleave");
this.$emit("afterLeave", e);
},
genTransition() { // ここは、親クラスと同じ内容をコピー
const content = this.genInnerContent();
if (!this.transition) return content;
return this.$createElement(
"transition",
{
props: {
name: this.transition,
origin: this.origin,
appear: true,
},
on:{ // afterLeaveイベントを追加
afterLeave:this.afterLeave
}
},
[content]
);
},
}
};
</script>
extendsの記載の詳細はVuetifyのFAQを参照。
(古いVuetifyだと、genTransitionが上書きできずrender自体を上書きするなどが必要になります。。。)
これにより、afterLeaveイベントを捕捉できるようになりました。
<template>
<TransitionableDialog v-model="dialog" width="500"
@afterLeave="print">
<template v-slot:activator="{ on, attrs }">
<v-btn v-bind="attrs" v-on="on">
印刷
</v-btn>
</template>
<div class="confirm">
印刷してよろしいですか?
<div>
<v-btn @click="dialog=false;closeWithOk=true">
はい
</v-btn>
<v-btn @click="dialog=false">
いいえ
</v-btn>
</div>
</div>
</TransitionableDialog>
</template>
<script>
data(){
return {
dialog:false,
closeWithOk:false
}
},
watch: {
dialog(v) {
if(v) {
this.closeWithOk = false
}
}
},
methods: {
print(e) {
if(!this.closeWithOk) {return}
window.print()
}
}
</script>
#Dialog以外のコンポーネント
Vuetifyでは、Dialog以外にVTabItemも同じように**「タブの表示が完了してから」**を捕捉するために、拡張して上書きしてなどできると思います。
VTabItemの場合には、親クラスのVWindowItemですでにonAfterTransitionがいるのでこいつを上書きし、最後にemitしてあげれば良いと思います。