はじめに
Rails7のアプリケーションに 星評価機能付きのコメント機能を実装した方法を紹介します。
今回はraty.js
を利用して、ユーザーがコメントを投稿する際に星評価をつけられるようにしました。
また、投稿されたコメントには星評価が表示されるようにしています。
本記事は、tweetsコントローラーのshowページ(投稿の詳細ページ)にコメント機能が実装されていることを前提に進行します。
開発環境
- Ruby 3.3.7
- Rails 7.1.3
- Node 18
- esbuildを使用したJavaScriptバンドル
1. raty.js の導入
まず、raty.js
をRails7のapp/javascript
ディレクトリに追加します。
1.JavaScript のセットアップ
app/javascript/raty.js
を作成し、以下のコードを記述します。
class Raty {
constructor(element, options = {}) {
this.element = element;
this.opt = {
...this.defaultOptions(),
...options,
...this._parseOptions(element.dataset)
};
this.init();
}
defaultOptions() {
return {
cancelButton: false,
cancelClass: 'raty-cancel',
cancelHint: 'Cancel this rating!',
cancelOff: 'cancel-off.png',
cancelOn: 'cancel-on.png',
number: 5,
readOnly: false,
score: undefined,
scoreName: 'comment[rate]', // フォームで使用するフィールド名
starHalf: '/assets/star-half.png',
starOff: '/assets/star-off.png',
starOn: '/assets/star-on.png',
target: "#comment_rate",
targetKeep: true,
};
}
init() {
this._setPath();
this._createStars();
this._createScoreField();
if (this.opt.readOnly) {
this._lock();
} else {
this._bindEvents();
}
this.setScore(this.opt.score);
}
setScore(score) {
score = this._adjustScore(score);
this._apply(score);
this._setHiddenField(score);
}
getScore() {
return this.scoreField ? this.scoreField.value : 0;
}
_apply(score) {
this._fillStars(score);
if (score) {
this.scoreField.value = score;
}
}
_setHiddenField(score) {
if (this.opt.target) {
const hiddenField = document.querySelector(this.opt.target);
if (hiddenField) {
hiddenField.value = score;
}
}
}
_fillStars(score) {
this.stars.forEach((star, index) => {
if (index + 1 <= score) {
star.src = this.opt.starOn;
} else if (index < score && index + 0.5 <= score) {
star.src = this.opt.starHalf;
} else {
star.src = this.opt.starOff;
}
});
}
_createStars() {
this.stars = [];
for (let i = 1; i <= this.opt.number; i++) {
const star = document.createElement("img");
star.src = this.opt.starOff;
star.dataset.score = i;
this.element.appendChild(star);
this.stars.push(star);
}
}
_createScoreField() {
this.scoreField = document.createElement("input");
this.scoreField.type = "hidden";
this.scoreField.name = this.opt.scoreName;
this.element.appendChild(this.scoreField);
}
_bindEvents() {
this.stars.forEach((star) => {
star.addEventListener("click", (event) => {
const score = event.target.dataset.score;
this.setScore(score);
});
});
}
_lock() {
this.stars.forEach(star => star.style.pointerEvents = "none");
}
_adjustScore(score) {
return Math.min(Math.max(parseFloat(score) || 0, 0), this.opt.number);
}
_setPath() {
this.opt.path = this.opt.path || '';
}
_parseOptions(dataset) {
return Object.keys(dataset).reduce((acc, key) => {
let value = dataset[key] === "true" ? true : dataset[key] === "false" ? false : dataset[key];
if (!isNaN(value) && Number.isInteger(parseFloat(value))) {
value = Number(value);
}
acc[key] = value;
return acc;
}, {});
}
}
export default Raty;
2.画像をapp/assets/imagesに追加
https://github.com/wbotelhos/raty/tree/main/src/images
star-on.png
, star-off.png
, star-half.png
をapp/assets/images/
に配置します。
3.モデルの修正
①Commentモデルにrateカラムを追加
rails g migration AddRateToComments rate:float
rails db:migrate
②Strong Parametersの修正
app/controllers/comments_controller.rb~にストロングパラメータ
rate`を許可します。
def comment_params
params.require(:comment).permit(:content, :rate)
end
4.Viewの修正
①show.html.erbの修正
app/views/tweets/show.html.erb
に以下のコードを追加します。
<% @comments.each do |c| %>
<div>
<%= c.user.email unless c.user.blank? %>
<%= c.content %>
<!-- 追加 -->
<div id="comment-<%= c.id %>-rating"></div>
</div>
<% end %>
<% if user_signed_in? %>
<div id="rating-form">
<%= form_with(model: [@tweet, @comment], local: true) do |f| %>
<%= f.text_area :content %>
<!-- 追加 -->
<%= f.hidden_field :rate, value: 0, id: "comment_rate" %>
<%= button_tag type: "submit" do %>
<i class="far fa-comments"></i> コメントする
<% end %>
<% end %>
</div>
<% end %>
初期化のスクリプトも追記します。
<script>
document.addEventListener("DOMContentLoaded", () => {
console.log("DOMContentLoaded イベント発火");
// フォームの星評価の初期化
const ratingForm = document.getElementById("rating-form");
if (ratingForm && !ratingForm.dataset.ratyInitialized) {
console.log("ratingForm に Raty を適用");
new Raty(ratingForm, {
starOn: "<%= asset_path('star-on.png') %>",
starOff: "<%= asset_path('star-off.png') %>",
starHalf: "<%= asset_path('star-half.png') %>",
scoreName: "comment[rate]"
});
ratingForm.dataset.ratyInitialized = "true"; // Raty が適用されたことを記録
}
// 各コメントの星評価の初期化
const comments = <%= raw(@comments.map { |c| { id: c.id, rate: c.rate || 0 } }.to_json) %>;
comments.forEach(comment => {
const commentRatingElement = document.getElementById(`comment-${comment.id}-rating`);
if (commentRatingElement && !commentRatingElement.dataset.ratyInitialized) {
console.log(`comment-${comment.id}-rating に Raty を適用`);
new Raty(commentRatingElement, {
starOn: "<%= asset_path('star-on.png') %>",
starOff: "<%= asset_path('star-off.png') %>",
starHalf: "<%= asset_path('star-half.png') %>",
readOnly: true,
score: comment.rate
});
commentRatingElement.dataset.ratyInitialized = "true"; // Raty が適用されたことを記録
}
});
});
</script>
5.Rails の設定
①manifest.jsの修正
app/assets/config/manifest.js
にapplication.js
を追加します。
//= link_tree ../images
//= link application.js
②application.jsにraty.js
を追加
app/javascript/application.js
に以下のコードを追記してください。
import Raty from "./raty";
window.Raty = Raty
これを行うことで、Railsのapplication.js
に自動的にRaty.js
が組み込まれます。
⑥動作確認
コメントを投稿して星評価が保存され、表示されるか確認
- rails db:migrate を実行
- rails s でサーバーを起動
チェックリスト
画像(星のアイコン)が上手く表示されない場合は以下を確認してみてください
✅ `app/assets/images`に`star-on.png`,`star-off.png`,`star-half.png`が存在しているか?
✅ `asset_path`または`image_tag`で正しいパスを指定しているか?
✅ `rails assets:precompile`を実行したか?
✅ `public/assets`にプリコンパイルされた画像が出力されているか?
✅ `config/environments/development.rb`で`config.assets.compile = true`になっているか?(本番環境では false にすることが推奨)
それでも上手くできない場合はデバックの出力やコンソールの実行結果を確認しながら、初期化が作動していないのか、コンパイルできていないのか、パスが正確でないのかなど、どこで問題が発生しているのか落ち着いて突き止めてくださいね。
まとめ
今回、Rails7で星評価付きのコメント機能を実装する方法をまとめました!
ご質問や改善点があれば、ぜひコメントしてください!😊