🌟

[長文]投稿に紐付いたコメントにraty.jsの⭐️評価をして送信したい。

2024/07/07に公開

前も応用課題でやってつまずいたんですがやっぱ星5段階難しいな
てか突然javaってのがやっぱ慣れない。前も記事書いたのにもう1回書いた理由↓↓

(前回)bookテーブルにstarカラムがある
(今回)postではなくcommentテーブルにstarカラムがある

そして今回多発したのは『これは果たしてpostかcommentどっちに書くの???』です。

こんがらがりそうだとは思ってたけどちゃんとコケまくりましたと🧎🧎(もはや才能)

https://zenn.dev/goldsaya/articles/2746dd2b886be0
今回もお世話になった記事。一番わかりやすい。ありがとうございますすぎ。
今回もこの記事あること前提で躓いたところだけ書いていきます。


⭐️未来の自分のために再度簡単なところも記入
まずダウンロード!(もうしてたらいらん!今回は入ってたん気づかずもう一回してた。まあええか)

ターミナル
yarn add jquery
config/webpack/environment.js
const { environment } = require('@rails/webpacker')
#追記
const webpack = require('webpack')

environment.plugins.prepend('Provide',
  new webpack.ProvidePlugin({
    $: 'jquery/src/jquery',
    jQuery: 'jquery/src/jquery',
    jquery: 'jquery/src/jquery',
  })
)
#ここまで
module.exports = environment
app/javascript/packs/application.js
省略

#追記
window.$ = window.jQuery = require('jquery');

ここまではスムーズ。だって写すだけ。


raty.jsの導入

早速躓いたところ🐥
https://github.com/wbotelhos/raty
こっからクローンしてね!うんうん。コードコピーするところまではええとして。。。。。どうやって???😇
ここで悩み倒しました。笑

ターミナル(普通に自分のアプリケーション)
git clone git@github.com:wbotelhos/raty.git
(SSHをコピーしました。都度違うかもやし確認すること)

そのあとの事前準備に関しては記事見て流れ通りにやってください。


投稿フォームに星レビュー機能の追加

(カラムに関してはもう作ってたので省略)

star"カラムを保存できるようにデータ操作を許可する。

これはまずカラムだから『comment controller』に書く

public/comment/controller
def comment_params
 params.require(:comment).permit(:comment, :star)#star追記
end

viewの記述

最初は共通テンプレート使用しようかと思ったけど、先にコメントのフォームを作っていて、

<%= form_with(model: [@post, Comment.new], local: true) do |form| %>
<% end %>

があるのにその中にrenderって無理だよね。となって諦めました。
まずは書いたコードから、、、

post/show.html.erb
<div class="form-group" id="star">
 <%= form.hidden_field :star, id: :post_star, class: 'form-control' %>
 <div id="post_raty"></div>
</div>
      
<script>
 $(document).on('turbolinks:load', function() {
 let elem = document.querySelector('#post_raty');
 if (elem == null) return;

 elem.innerHTML = "";
 let opt = {
  starOn: "<%= asset_path('star-on.png') %>",
  starOff: "<%= asset_path('star-off.png') %>",
  scoreName: 'comment[star]', // フォームのscoreとして保存するフィールド名
 };
  raty(elem, opt);
 });
</script>

自分なりの解説

まずここから
<div class="form-group" id="star">
 <%= form.hidden_field :star, id: :post_star, class: 'form-control' %>
 <div id="post_raty"></div>
</div>
"id="star"はなんのためにある??

id="star"はこの要素をJavaScriptで操作するための識別子。
正直ここはなくてもいいけどもし同じようなことを同じページとかでする場合こうやって分けといた方が無難。

2行目のid="post_starは??

[結論]=任意のIDをつけてる
隠しフィールドには通常、ユーザーが操作することが意図されていないデータが含まれている。
hidden_fieldにidとか名前つけてエラー回避したりフォームデータの送信やJavaScriptによる操作などがスムーズに行えるようにしてる。ふーん🧐

<div id="post_raty"></div> ????

ここに星評価のUI(ユーザーインターフェース)が表示される
(ユーザーインターフェース)とは、ユーザーが星をクリックして評価を付けることができるインタラクティブな要素を指す。
id="post_star"はJavaScriptで値を操作するための識別子

<script>
 $(document).on('turbolinks:load', function() {
 let elem = document.querySelector('#post_raty');
 if (elem == null) return;

 elem.innerHTML = "";
 let opt = {
  starOn: "<%= asset_path('star-on.png') %>",
  starOff: "<%= asset_path('star-off.png') %>",
  scoreName: 'comment[star]', // フォームのscoreとして保存するフィールド名
 };
  raty(elem, opt);
 });
</script>
$(document).on('turbolinks:load', function() { ... });

これは、JavaScriptやjQueryでよく使われる構文。
特にRailsアプリケーションではTurbolinksと一緒に使用されます。
Turbolinksは、ページを高速に遷移するためのライブラリで、ページ全体をリロードせずに、HTMLの一部だけを更新できる!!!
つまりこのコードの目的は、ページが完全に読み込まれた後に特定のJavaScriptコードを実行することです。
まあrails内でjavaを使うときによく使われるテンプレートみたいなもん!!!!

let elem = document.querySelector('#post_raty');の役割(⭐️A)

簡単にいうと要素の取得(今回はpost_raty)ちなみに任意。
querySelectorメソッドを使用して、(#post_raty)というIDを持つ要素を取得してる。
後で出てくる!(⭐️Aとする)

❶ if (elem == null) return; と ❷ elem.innerHTML = "";の役割

❶要素の存在確認
elemが存在しない場合、スクリプトを終了します。
❷要素の初期化
elemの内容を空にします。
これが無いと戻ってきた時に、エラーじゃ無いけど星が15個になってた😴
これにより、既存のコンテンツがクリアされ、新しい星評価のUIが表示されます。
この2つがあることでこれにより、❶要素が見つからないときにエラーが発生するのを防いだり❷予期しないバグや表示上の問題を防ぐ。

let opt = { ... };

オプションの設定をする。aseets/imagesの中に対象の画像を入れといて
starOn: 星がオンの状態とstarOff: 星がオフの状態の画像のpassを指定する。

scoreName: 'comment[star]',とは??

保存先を指定するもの。今回はCommentモデルのstarカラムに星評価のデータを送信したいので
'comment[star]'と記載する!!!ほえー🫨

raty(elem, opt);(⭐️A)

elemに対して指定されたオプション opt を使用して星評価UIを初期化するってコード。
elemはHTML要素を指す(この場合は#post_raty)上で定義した(⭐️Aの所)
opt(オプション)→starOnとかscoreNameとか!!!
つまりこのコードによって
#post_ratyという要素に対して、指定された設定で星評価のUIが表示されるようになる!!!
表示場所は、<div id="post_raty"></div>

まとめ

<1、ページの読み込み完了時に実行される処理>
Turbolinksを使用している場合、ページの読み込みが完了するとこの処理が実行されます。

<2、要素の取得と初期化>
任意(#post_raty)というIDを持つ要素を取得。
もし要素が存在しない場合、処理を中止します。

<3、要素の内容のクリア>
取得した要素の中身を空にします。これにより、後続の操作がクリーンな状態から始めれる

<4、星評価のUIの設定>
starOnとstarOffというオプションで、星のON状態とOFF状態の画像パスを指定。
scoreNameでどこに送るか指定。

<5、raty関数の実行>
raty(elem, opt);により、取得した要素に対してratyプラグインを適用。
これにより、ユーザーが星評価を選択できるUIが表示される。

この流れによってユーザーが星評価を選択し、その評価がフォーム経由で保存される仕組みが構築される。


出力

<div id="star_<%= @post.id %>_<%= comment.id %>"></div>
  <script>
   $(document).on('turbolinks:load', function() {
   let elem = document.querySelector('#star_<%= @post.id %>_<%= comment.id %>');
   if (elem == null) return;

    let opt = {  
      starOn: "<%= asset_path('star-on.png') %>",
      starOff: "<%= asset_path('star-off.png') %>",
      score: '<%= comment.star %>',
      readOnly: true,
    };
      raty(elem, opt);
   });
</script>

やっと出力〜〜〜〜ポイントだけおさらい

<div id="star_<%= @post.id %>_<%= comment.id %>"></div>

@post.id と comment.id を使用して、ユニーク(一意性)なIDを生成しています。
ちなみにstar_は任意

これは

let elem = document.querySelector('#star_<%= @post.id %>_<%= comment.id %>');

のSelectorでも同じ('#star_<%= @post.id %>_<%= comment.id %>')が選ばれてる。

readOnly

読み取り専用。出力の時に追記するコード

なんとかできたあああああああああああああ。
地獄でした🍵マジでいつか役に立ってくれ。⭐️⭐️

おまけエラー話🤥

やっと終わって『さぁ、commitしよ』と思ったらgit addができない。。。
調べたら他のユーザーのgithubあるけどどないする?2人は無理やで。と。。
クローンしたからや!!!!😨え?消したらいいの?そしたら全部消えるとかないよな?いや怖すぎると思って勇気が出ずまさかの30分悩み続けた結果メンターさんに一緒にやってもらうことにしました(すみません)

結論から言うとちゃんとraty.jsを自分のjavaにコピーして貼り付けて必要な画像もaseets/imagesに入れてたから消しても問題ないとのことでした。よかったーーーーーーーー

Discussion