1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

vue-clipboard2をVuetifyのv-dialogの中で動かす

Last updated at Posted at 2020-11-06

システムから発行したIDやパスワードなどを、ユーザーが画面上で簡単にクリップボードにコピーできるようにしたいなと思って実現方法を調べていたところvue-clipboard2というモジュールが見つかったので使ってみることにしました。普通に使えばドキュメント通りに実装すれば動くと思うのですが、Vuetifyのv-dialogの中で使おうとするとかなり手こずったので、動かす方法をまとめます。

環境

  • Vue.js 2.6.11
  • Vuetify 2.2.11
  • TypeScript 3.9.3
  • vue/cli
  • クラスベース記法

vue-clipboard2とは

Inndy/vue-clipboard2

Vueでクリップボードへテキストをコピーできるモジュールです。ボタンをクリックするとテキストをクリップボードにコピーする、といったことが(通常は)簡単にできます。

セットアップは以下の通り。

$ npm install vue-clipboard2
main.ts
import VueClipboard from 'vue-clipboard2';
Vue.use(VueClipboard);

問題

ドキュメントにbootstrapのmodal内だと細工しないと動かないよと書いてありますが、Vuetifyのv-dialogでも当然のように動きませんでした。対処法は探すといくつか見つかりはしますが、TypeScriptを使っている事例がなかなかなく、かなり試行錯誤が必要でした。

解決方法

2つある使い方のうちv-clipboard:copyを使う方法だとどうやっても駄目でした。

$copyTextを使う場合、以下のように実装することで動かすことができました。

<template>
  <v-dialog>
    <v-card>
      <v-card-text>
        <v-text-field :value="textToCopy" readonly/>
        <v-btn icon ref="copyBtn" @click="onCopy"><v-icon>mdi-content-copy</v-icon></v-btn>
      </v-card-text>
    </v-card>
  </v-dialog>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';

@Component
export default class SampleDialog extends Vue {
  textToCopy = 'some text to copy';

  private onCopy(): void {
    const isVue = (x: unknown): x is Vue => x instanceof Vue;
    const { copyBtn } = this.$refs;
    if (!isVue(copyBtn)) {
      return;
    }
    this.$copyText(this.textToCopy, copyBtn.$el);
  }
}
</script>

ハマりどころは$copyTextの第2引数でした。ドキュメントにはcontainerを渡せと雑に書いてはあるものの、実際何型の何を渡せばいいのやら、それらしい可能性を片っ端から試していって上記のコードに辿り着きました。

ボタンにコピー機能を仕込む場合はそのボタンをコンテナとして指定すればいいようです。型はVeturのコード補完ではobject | HTMLElementと出てきますがElement型のオブジェクトを渡すとうまくいきました。

ボタンをElementとして取得する方法も(Vue慣れてる人だと簡単かもしれませんが)結構悩みました。

v-btnタグにref属性を付けるとthis.$refs経由で参照できるようになりますが、その際の型はVue | Element | Vue[] | Element[]です。上記のコードの場合copyBtnで実際に参照できるオブジェクトの型はVueですが(v-btnもコンポーネントである以上Vueを継承している形になっている)、TypeScript的には使う前にタイプガードで型をVueに絞ってあげる必要がありました。(x: unknown): x is Vue => x instanceof Vueのところですね。このチェックを通すことで型が保証され、以降はVue型として扱えるようになります。あとはVue$elプロパティがElementなので、それを$copyTextの第2引数に渡せばTHE ENDにゃん。

ちなみにVueClipboard.config.autoSetContainer = true;という方法も紹介されていますが、こちらはうまくいきませんでした。

もうひとつちなみに、ドキュメントには非同期関数内で使わないでねと書いてありますが、上記コードのonCopyasync付けても動きました。$copyTextPromiseを返すので、処理結果までちゃんと見たい場合はconst result = await this.$copyText(...);のように書くことができます。もちろんthis.$copyText(...).then(...).catch(...);で書いてもOKです。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?