前文
しばらく使わないうちに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
へ保存する。
コントローラーは以下のようになった。
# 略
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モデルに書いている。
#略
def self.convert_content_shared(formatted_para, genres_set)
# すっごい、いろいろ省略
date + "\n" + formatted_items + "\n" + content
end
整形された文字を戻り値としている。
3.日報一覧画面にコピーボタンを作る
viewファイルにボタンを作る。
<!-- 略 -->
<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でクリップボードへコピー
ボタンが押されたときにイベントを作る。
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)
を使うだけで良い。
おわり
これでコピーする機能を実装できた。これから詰まったところを紹介していく。書いておくと、自分で忘れた時も便利でいいなあ。