📗

Vue3でTabulatorを動的にマウントしたい

2022/10/06に公開

まだ勉強を始めたばかりなので何か変なところがあれば教えてください

やりたいことと現状

  • TabulatorをVue3で使う
    • ドキュメントを見るとComponentは無く、refから直接マウントする感じ
  • Vuetify(beta)のv-dialogの中でTabulatorを使いたい(初期は非表示)
  • とりあえずmountedの中でTabulatorをマウントするとエラーが出る
script setup lang="ts"

import { ref, onMounted } from "vue";
import { TabulatorFull as Tabulator } from "tabulator-tables";

const isShow = ref(false);
const table = ref<HTMLElement>();
const tabulator = ref<Tabulator>();

onMounted(() => {
  tabulator.value = new Tabulator(table.value!); //ダメ 
});
template
  <v-btn @click="isShow = true">開く</v-btn>
  <v-dialog v-model="isShow">
    <v-card>
      <v-card-item>
        <div ref="table"></div>
      </v-card-item>
    </v-card>
  </v-dialog>

マウントされた時点でdivは居ないので、Tabulatorを割り当てできない(そりゃそうだ)

試したこと

v-showを使う(ダメ)

v-showを使って、Componentのマウント時にtableが居るけど、見えてないという状態を作る作戦。

v-dialogv-showで可視をコントロール出来ないっぽいのでダメでした。

ちなみに普通のComponentならできました。v-showが使えるならこれが楽かもしれません。

マウント用のメソッドを作る

大人しくメソッドを作り、その中でTabulatorをマウントする。

script setup lang="ts"
import { ref, nextTick } from "vue";
import { TabulatorFull as Tabulator } from "tabulator-tables";

const isShow = ref(false);
const table = ref<HTMLElement>();
const tabulator = ref<Tabulator>();

const tabulatorMount = async () => {
  isShow.value = true;
  await nextTick(); // tableが出てくるまで待つ
  tabulator.value = new Tabulator(table.value!);
}
template
  <v-btn @click="tabulatorMount"></v-btn>
  <v-dialog v-model="isShow">
    <v-card>
      <v-card-item>
        <div ref="table">開く</div>
      </v-card-item>
    </v-card>
  </v-dialog>

nextTickでtableが出るまで待ってから、Tabulatorをマウントしました。(本当はちゃんとtableの存在確認をした方が良いかも)

消すとき

消すときも適切に処理しないとエラーが出ます。

script setup lang="ts"
import { ref, nextTick } from "vue";
import { TabulatorFull as Tabulator } from "tabulator-tables";

const isShow = ref(false);
const table = ref<HTMLElement>();
-const tabulator = ref<Tabulator>();
+const tabulator = ref<Tabulator | null>(); // nullable

const tabulatorMount = async () => {
  isShow.value = true;
  await nextTick(); // tableが出てくるまで待つ
  tabulator.value = new Tabulator(table.value!);
}

+const tabulatorUnMount = () => {
+  tabulator.value = null; // tableを消す前にTabulatorを削除しておく
+  isShow.value = false;
+}
template
  <v-btn @click="tabulatorMount">開く</v-btn>
  <v-dialog v-model="isShow">
    <v-card>
      <v-card-item>
        <div ref="table"></div>
      </v-card-item>
+      <v-card-actions>
+        <v-btn @click="tabulatorUnMount">閉じる</v-btn>
+      </v-card-actions>
    </v-card>
  </v-dialog>

nullで無理やり削除しているのでちょっと微妙な感じがしますが、とりあえず動きます。
(追記)閉じるボタンの配置がダイアログの外だったので、修正しました

まとめ

  • refからマウントするライブラリは、ちゃんとref元のHTMLElementがDOMに出てきた後にマウントする
  • 消すときも同様にDOMから無くなる前にアンマウントする

結構面倒くさいのでこれ系統のライブラリは単体でComponentを作った方が良いなぁと思いました。

残された謎

  • TabulatorのreactiveDataをtrueにして実行すると、削除・インスタンス生成を繰り返す度に重くなって、4回目あたりでフリーズします。もうちょっと追えば原因分かりそうですが、とりあえず無くても動くので、また今度。

Discussion