星レビュー機能の実装をしてみました。
ルーティングのネストを理解していれば、そこまで複雑ではありません。
ただ、form_withによるデータの流れがわからなかったので苦戦しました。
fontawsomeの導入が必要ですが、その方法は割愛します。
では、みていきましょう。今回はyoutube動画を参考にさせていただきました。
まずはルーティングです。
今回は誰かの投稿に対して、星5つで評価できるように実装しました。
routes.rb
resources :posts do
resource :reviews, only:%i[create]
end
以下のルーティングができました。
色々な投稿の中のどの投稿にレビューするか可能になります。
rails routes | grep reviews
post_reviews POST /posts/:post_id/reviews(.:format) reviews#create
今回は、form_withに最も苦戦しました!
正しいかわからないのですが、個人的な解釈を書いておきたいと思います。
views/posts/show.html.erb
<%= form_with model:@review, url:post_reviews_path(@post), local: true do |f| %>
<div class="main-rating">
<div class="ratings">
<span class="fa fa-star-o" id="star"></span>
<span class="fa fa-star-o" id="star"></span>
<span class="fa fa-star-o" id="star"></span>
<span class="fa fa-star-o" id="star"></span>
<span class="fa fa-star-o" id="star"></span>
</div>
<div class="rating-value-display"><span id="rating-value-display">0</span>/5</div>
</div>
<%= f.hidden_field :score, id:"rating_value" %>
<%= f.text_area :content %>
<%= f.submit "評価"%>
<% end %>
<%= javascript_pack_tag 'review_stars' %>
url:post_reviews_path(@post)
formのデータをどこに飛ばすかを示しています。
この場合は、reviewsコントローラーのcreateアクションに飛ばします。
その時にpost_idのデータが必要なので、@postを渡しています。
これは正確には@post.idだったはず。
@postについては、postsコントローラーのshowアクションに
@post = Post.find(params[:id]) を記述しておく。
@review
reviewsコントローラーに飛んだ際に、どのモデルを参照するか示しています。
今回は、新たにデータを保存していきます。
def create
@review = Review.new(review_params)
end
private
def review_params
params.permit(:post_id, :score, :content)
end
app/javascript/packs/review_stars.js
const stars = document.querySelector(".ratings").children;
const ratingValue = document.querySelector("#rating_value");
const ratingValueDisplay = document.querySelector("#rating-value-display");
let index;
for(let i=0; i < stars.length; i++){
stars[i].addEventListener("mouseover", () => {
for(let j=0; j<stars.length; j++){
stars[j].classList.remove("fa-star")
stars[j].classList.add("fa-star-o")
}
for(let j=0; j<=i; j++){
stars[j].classList.remove("fa-star-o");
stars[j].classList.add("fa-star");
}
})
stars[i].addEventListener("click", () => {
ratingValue.value = i+1;
ratingValueDisplay.textContent = ratingValue.value;
index = i;
})
stars[i].addEventListener("mouseout", () => {
for(let j=0; j<stars.length; j++){
stars[i].classList.remove("fa-star");
stars[i].classList.add("fa-star-o");
}
for(let j=0; j <=index; j++){
stars[j].classList.remove("fa-star-o");
stars[j].classList.add("fa-star");
}
})
}