LoginSignup
1
0

More than 1 year has passed since last update.

JSを使ってクリップボードへコピーする機能を実装した

Posted at

前文

 しばらく使わないうちにQiitaのエディタが新しくなっていた。書き心地がいい。

 個人開発アプリに、日報に登録した文面をクリップボードへコピーする機能を追加した。twitterやSlackなどへシェアしたいときに便利だ。実装の仕方をここにまとめる。

0. 流れ

 1.DBにコピー用の文章を保存するカラムを追加
 2.日報を登録時にコピー用の文章も保存
 3.日報一覧画面にコピーボタンを作る
 4.JSでクリップボードへコピー

1.DBにコピー用の文章を保存するカラムを追加

日報のマイグレーションファイルはこのようになった。日報の詳細文章である content の他にcontent_for_shareを追加している。

class CreateReports < ActiveRecord::Migration[6.0]
  def change
    create_table :reports do |t|
      t.references :user, null: false, foreign_key: true
      t.datetime :reported_on, null: false
      t.string :content, null: false
      t.string :content_for_share
      t.timestamps
    end
  end
end

2.日報を登録時にコピー用の文章も保存

 日報を登録するときに、コピー用の文章を整形して、content_for_shareへ保存する。

コントローラーは以下のようになった。

app/controllers/reports_controller.rb
# 略

  def create
    # 略

    formatted_para = report_params
    formatted_para[:report_items_attributes] = para
    genres_set = get_genre_nameset

    share_content = Report.convert_content_shared(formatted_para, genres_set)
    @report.content_for_share = share_content
    # 以降、日報の保存処理

  end

formatted_paraの中身はちょっとわかりにくい。最終的にはこのようになっている。

> formatted_para
=> <ActionController::Parameters {
    "content"=>"本日のコメント", 
    "reported_on"=>"2023-04-23", 
    "report_items_attributes"=><ActionController::Parameters {
        "0"=><ActionController::Parameters {
            "content"=>"やったこと1", 
            "genre_name"=>"Rails", 
            "genre_id"=>1
        } permitted: true>, 
        "1682190268018"=><ActionController::Parameters {
            "content"=>"やったこと2",
            "genre_name"=>"個人開発",
            "genre_id"=>10
        } permitted: true>
    }
     permitted: true>
} permitted: true>

genres_setはこう

> genres_set
=> [[1, "Rails"], [2, "英語"], [3, "Vue"], [10, "個人開発"]]

convert_content_sharedメソッドはReportモデルに書いている。

app/models/report.rb
#略

  def self.convert_content_shared(formatted_para, genres_set)
    # すっごい、いろいろ省略
    date + "\n" + formatted_items + "\n" + content
  end

整形された文字を戻り値としている。

3.日報一覧画面にコピーボタンを作る

 viewファイルにボタンを作る。

app/views/reports/_reportlist.html.erb
<!-- 略 -->
      <div class="col-4 col-sm-3 col-xl-2">
        <button type="button" class="btn btn-sm btn-outline-primary js-copy-report" data-report-id="<%= report.id %>"><i class="far fa-clipboard mr-1"></i>COPY</button>
        <div class="d-none" share-report-id="<%= report.id %>"><%= report.content_for_share %></div>
      </div>
<!-- 略 -->

 ポイントはコピー用の文章を非表示で載せること。これを後の処理で使う。bootstrapを使っているのでbtn-sm のようなclass名になっている。

4.JSでクリップボードへコピー

 ボタンが押されたときにイベントを作る。

app/javascript/home.js
  const copyReportButtons = document.querySelectorAll('.js-copy-report');
  if (document.querySelectorAll('.js-copy-report') !== null){
    copyReportButtons.forEach(function(button) {
      const reportId = button.dataset.reportId;

      button.addEventListener('click', function(event) {
        event.preventDefault();
        button.textContent = 'Copied!';
        setTimeout(() => {
          button.innerHTML = `<i class="far fa-clipboard mr-1"></i>COPY`;
        }, 1000);
        copyText = document.querySelector(`[share-report-id="${reportId}"]`).textContent;
        copyToClipboard(copyText);
      });
    });
  }


function copyToClipboard(text) {
  navigator.clipboard.writeText(text)
    .then(() => {
      // console.log('Copied to clipboard');
    })
    .catch((error) => {
      // console.error('Failed to copy to clipboard', error);
    });

js-copy-reportがついているボタン要素ひとつひとつにイベント追加していく。ボタンのreportidを読み取り、viewファイルに記述されている日報のデータと紐づける。非表示だが記入はされているので、わざわざDBに取得に行かなくて良い。

 ボタンが押されたら少しの間、文字がCopied!になる。
 
 クリップボードへのコピーはnavigator.clipboard.writeText(text) を使うだけで良い。

おわり

 これでコピーする機能を実装できた。これから詰まったところを紹介していく。書いておくと、自分で忘れた時も便利でいいなあ。

1
0
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
0