Rails不使用act_as_votable实现无刷新点赞功能求助
实现自定义投票系统的无刷新更新(无需act_as_votable)
我来一步步帮你搞定无刷新投票功能,完全适配你自己写的投票逻辑,不用依赖第三方gem。咱们从视图、控制器到JS脚本一步步调整:
1. 修改视图代码:添加AJAX支持与唯一标识
首先要让投票请求变成异步的,还要给每个Tip的元素加唯一ID,这样JS才能精准定位到当前操作的Tip。修改后的ERB代码如下:
<% @tips.limit(5).order('votes DESC').each do |tip| %> <td id="votebox_<%= tip.id %>"> <div> <%= link_to image_tag('', class: "uparrow"), tip_upvote_path(tip), method: :put, class: "tip_upvote", remote: true, data: { tip_id: tip.id } %> </div> <div> <%= link_to image_tag('', class: "downarrow"), tip_downvote_path(tip), method: :put, class: "tip_downvote", remote: true, data: { tip_id: tip.id } %> </div> </td> <td class="vote_count" id="vote_count_<%= tip.id %>"><%= tip.votes %></td> <% end %>
这里的关键修改:
- 添加
remote: true,让链接发起AJAX请求而非页面跳转 - 给
votebox和vote_count添加带tip.id的唯一ID,方便JS定位 - 给投票按钮加
data-tip-id属性,辅助JS识别当前操作的Tip
2. 更新控制器:支持JS响应并传递数据
调整upvote和downvote方法,添加JS格式的响应,同时把最新的投票数据传给前端:
点赞控制器代码
def upvote @tip = Tip.find(params[:id]) ip = request.ip @updated = false # 标记是否实际执行了投票操作 unless Vrecord.exists?(:ip => ip, :tip_id => @tip.id, :pos => "u") Tip.increment_counter(:votes, @tip.id) @tip.reload # 重新加载最新的投票数 # 删除已存在的点踩记录 Vrecord.where(:ip => ip, :tip_id => @tip.id, :pos => "d").delete_all # 创建新的点赞记录 Vrecord.create(:ip => ip, :tip_id => @tip.id, :pos => "u") @updated = true end respond_to do |format| format.html { redirect_to champion_path(ch, anchor:'countertip') } format.js # 自动渲染对应的upvote.js.erb end end
点踩控制器代码(类似调整)
def downvote @tip = Tip.find(params[:id]) ip = request.ip @updated = false unless Vrecord.exists?(:ip => ip, :tip_id => @tip.id, :pos => "d") Tip.decrement_counter(:votes, @tip.id) @tip.reload Vrecord.where(:ip => ip, :tip_id => @tip.id, :pos => "u").delete_all Vrecord.create(:ip => ip, :tip_id => @tip.id, :pos => "d") @updated = true end respond_to do |format| format.html { redirect_to champion_path(ch, anchor:'countertip') } format.js end end
3. 编写JS脚本:实现无刷新更新
在app/views/tips/目录下创建两个JS.erb文件,分别处理点赞和点踩后的页面更新:
upvote.js.erb
// 只有当投票操作实际执行时才更新页面 if (<%= @updated.to_json %>) { // 更新投票计数 const voteCountEl = document.getElementById('vote_count_<%= @tip.id %>'); voteCountEl.textContent = <%= @tip.votes %>; // 可选:更新按钮状态(比如禁用点赞,恢复点踩) const upBtn = document.querySelector('.tip_upvote[data-tip-id="<%= @tip.id %>"]'); const downBtn = document.querySelector('.tip_downvote[data-tip-id="<%= @tip.id %>"]'); upBtn.classList.add('disabled'); downBtn.classList.remove('disabled'); }
downvote.js.erb
if (<%= @updated.to_json %>) { // 更新投票计数 const voteCountEl = document.getElementById('vote_count_<%= @tip.id %>'); voteCountEl.textContent = <%= @tip.votes %>; // 可选:更新按钮状态 const downBtn = document.querySelector('.tip_downvote[data-tip-id="<%= @tip.id %>"]'); const upBtn = document.querySelector('.tip_upvote[data-tip-id="<%= @tip.id %>"]'); downBtn.classList.add('disabled'); upBtn.classList.remove('disabled'); }
额外优化:添加按钮状态样式
可以给.disabled类加CSS样式,让用户直观看到按钮状态:
.disabled { opacity: 0.5; cursor: not-allowed; pointer-events: none; /* 阻止点击事件 */ }
内容的提问来源于stack exchange,提问作者kydronin
相关产品推荐
相关产品推荐

