Rails实现无刷新点赞/取消点赞功能技术求助
Rails实现无刷新点赞/取消点赞功能求助
我正在搭建一个包含帖子(Post)和点赞(Like)模型的网站,需求是用户仅能对帖子点赞一次,点赞后按钮变为“取消点赞”,且操作时无需跳转页面即可更新状态。目前实现有难度,希望得到指引。
现有代码
点赞按钮模板 /likes/_likes.html.erb
<% liked = @post.likes.find { |like| like.user_id == current_user.id} %> <div class="likes"> <% if liked %> <%= button_to 'Unlike', post_like_path(@post, liked), method: :delete %> <% else %> <%= button_to 'Like', post_likes_path(@post), method: :post %> <% end %> <%= @post.likes.count %><%= (@post.likes.count) == 1 ? 'Like' : 'Likes'%> </div>
Likes控制器代码
class LikesController < ApplicationController before_action :find_post before_action :find_like, only: [:destroy] def create if (!already_liked?) @post.likes.create(user_id: current_user.id) end end def destroy if (already_liked?) @like.destroy end end private def already_liked? Like.where(user_id: current_user.id, post_id: params[:post_id]).exists? end def find_post @post = Post.find(params[:post_id]) end def find_like @like = @post.likes.find(params[:id]) end end
包含点赞部分的视图代码
<div class="post-display"> <% if @post.title %> <h1><%= @post.title %></h1> <% end %> <% if @post.user %> Post by <%= @post.user.email %> <% end %> <% if @post.price %> <p>$<%= sprintf "%.2f", @post.price %></p> <% end %> <% if @post.description %> <p><%= @post.description %></p> <% end %> <% if @post.image.present? %> <%= image_tag @post.image.variant(:small) %> <% end %> <%= render 'likes/likes' %> </div> <% if current_user == @post.user %> <%= link_to "Edit", edit_post_path(@post) %> <%= button_to "Delete", @post, method: :delete %> <% end %> <% if @post.comments.count > 0 %> <div class="post-comments"> <h2 class="post-comments-headline">Comments</h2> <%= render @post.comments %> </div> <% end %> <h2>Add a comment:</h2> <%= render 'comments/form' %>
无刷新点赞实现步骤
1. 修改控制器支持JS响应
在create和destroy方法末尾添加响应JS的逻辑,让控制器能返回AJAX所需的内容:
def create if (!already_liked?) @post.likes.create(user_id: current_user.id) end respond_to do |format| format.js end end def destroy if (already_liked?) @like.destroy end respond_to do |format| format.js end end
2. 创建JS模板更新页面
在app/views/likes/目录下创建两个JS模板文件,用于替换页面上的点赞区域:
create.js.erb
// 用最新的点赞模板替换页面中的点赞区域 $('.likes').html("<%= j render 'likes/likes' %>");
destroy.js.erb
// 同样替换点赞区域的内容 $('.likes').html("<%= j render 'likes/likes' %>");
注:j是escape_javascript的简写,用于转义HTML内容,避免JS语法错误。
3. 给按钮添加AJAX属性
修改_likes.html.erb中的button_to,添加remote: true让Rails用AJAX发送请求:
<% liked = @post.likes.exists?(user_id: current_user.id) %> <div class="likes"> <% if liked %> <%= button_to 'Unlike', post_like_path(@post, liked), method: :delete, remote: true %> <% else %> <%= button_to 'Like', post_likes_path(@post), method: :post, remote: true %> <% end %> <%= @post.likes.count %><%= @post.likes.count == 1 ? ' Like' : ' Likes'%> </div>
这里顺便优化了点赞判断逻辑,用exists?比find更高效,无需加载整个点赞对象。
4. 确保页面引入必要的JS库
确认application.js中包含jQuery和Rails UJS(Rails 7+可用Turbo替代,将remote: true换成data: { turbo: true }即可):
//= require jquery //= require rails-ujs
代码优化建议
- 模型层关联优化
在Post模型中添加辅助方法,简化点赞判断:
class Post < ApplicationRecord has_many :likes, dependent: :destroy def liked_by?(user) likes.exists?(user_id: user.id) end end
之后在视图中可直接用@post.liked_by?(current_user)替代原有判断,代码更清晰。
- 控制器逻辑简化
用模型层的方法替换控制器中的already_liked?:
def already_liked? @post.liked_by?(current_user) end
- 视图代码简化
用Rails内置的pluralize方法简化点赞计数显示:
<%= pluralize(@post.likes.count, 'Like') %>
一行代码即可替代原有的计数+单数/复数判断。
- 数据库层面防重复
在Like模型添加唯一性验证,确保数据库层也能阻止重复点赞:
class Like < ApplicationRecord belongs_to :post belongs_to :user validates :user_id, uniqueness: { scope: :post_id } end
内容的提问来源于stack exchange,提问作者Jillian Hoenig
相关产品推荐
相关产品推荐

