VuetifyでnextTickしても、良いタイミングで処理が動かないときの対応（transition）


#nexttickの使い方

Vuejsでは、ある値を変更し、nexttickをすることで、リアクティブに動く値が変更したあとの処理を実行することができます。


```vuejs:sample.vue
<template>
 <div id='name'>{{name}}</div>
</template>
<script>
methods: {
  setNameAndSayHello(name) {
    this.name = name
    console.log(document.getElementById('name').innerHTML)
  }
}
</script>
```
setNameAndSayHello()の中で参照されているDOMはまだ描画されていないです。そのためまだ古い値が使われています。

```vuejs:sample.vue
<script>
methods: {
  setNameAndSayHello(name) {
    this.name = name
    this.$nextTick(function () {
        console.log(document.getElementById('name').innerHTML)
    })
  }
}
</script>
```
nextTickの中で実施することで、描画されたあとの値を利用することができます。

#VuetifyのVDialogでダイアログ閉じたあとの処理の実装をnextTickでやった場合
印刷確認ダイアログを実装したく、以下のようにv-dialogを使い実装しました。

```vuejs:PrintDialog.vue
<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()を使うことです。

```vuejs:PrintDialog.vue
<script>
methods: {
  print(e) {
    this.dialog=false
    setTimeout(()=> {
      window.print()
    }, 500)
  }
}
</script>
```
これでダイアログが閉じたときぐらいに、print()処理が呼び出されて、無事にダイアログを除いて印刷されます。
でもこれではちゃんとDOMがなくなったをしっかり捕捉できているかわからないです。


#VuetifyのCSSアニメーションの実装
Vuetifyの[VDialog](https://github.com/vuetifyjs/vuetify/blob/master/packages/vuetify/src/components/VDialog/VDialog.ts#L276)は、VueのTransitionにより実装されています。

VueのTransitionだと、ダイアログが閉じた場合をハンドリングするには、[afterLeave](https://jp.vuejs.org/v2/guide/transitions.html#JavaScript-%E3%83%95%E3%83%83%E3%82%AF)を指定すれば良いのですが、それが実装されていないです。。。

そこで以下のようなクラスを作ります。

```vuejs:TransitionableDialog.vue
<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](https://vuetifyjs.com/en/getting-started/frequently-asked-questions/#extend-components)を参照。
(古いVuetifyだと、genTransitionが上書きできずrender自体を上書きするなどが必要になります。。。)

これにより、afterLeaveイベントを捕捉できるようになりました。

```vuejs:ConfirmDialog.vue
<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](https://github.com/vuetifyjs/vuetify/blob/master/packages/vuetify/src/components/VTabs/VTabItem.ts)も同じように**「タブの表示が完了してから」**を捕捉するために、拡張して上書きしてなどできると思います。
VTabItemの場合には、親クラスの[VWindowItem](https://github.com/vuetifyjs/vuetify/blob/master/packages/vuetify/src/components/VTabs/VTabItem.ts)ですでにonAfterTransitionがいるのでこいつを上書きし、最後にemitしてあげれば良いと思います。