Rails 6 中使用jQuery多位置渲染模型及Kaminari分页重复问题求解
问题根因
你写在首页<script>标签内的render_partial函数会在页面首次加载时完成ERB插值渲染,此时@posts固定为第一页的分页数据,后续点击按钮时不会重新请求后端更新@posts的值,因此永远追加的是第一页内容。放在home.js.erb中可以正常运行,是因为该文件是每次点击分页按钮发起远程请求后,由后端动态渲染返回的,会携带对应页码的@posts数据。
注:你当前控制器的分页逻辑存在逻辑错误,第一条排序的@buffers赋值会被第二条where查询完全覆盖,排序逻辑没有生效,下文方案会同步修正该问题。
可行解决方案
方案1:控制器内直接渲染JS响应,无需新增JS模板文件
直接在控制器的action中定义JS响应逻辑,不需要单独创建.js.erb模板文件:
# app/controllers/page_controller.rb def home if "default" == @tab_id || "PostForYou" == @tab_id # 修正逻辑:先筛选一周内帖子,再按热度排序 @buffers = Post.where("created_at >= ?", 1.week.ago.utc) .sort_by { |post| cal_post_hot_point(post) } .reverse @posts = Kaminari.paginate_array(@buffers).page(params[:page]).per(10) end respond_to do |format| format.html # 普通页面请求走HTML响应 format.js do # 直接渲染JS逻辑,无需单独模板文件 render js: <<~JS // 追加新分页的帖子内容 $("<%=j render @posts %>").appendTo($("#home_infinite_scroll")); // 更新分页按钮 <% if @posts.last_page? %> $('#home_pagination').html("<a>End</a>"); <% else %> $('#home_pagination').html("<%=j link_to_next_page(@posts, 'Next', remote: true) %>"); <% end %> JS end end end
修改完成后,删除首页中原有的render_partial函数代码,同时去掉link_to_next_page的onclick参数即可正常使用。
方案2:通用前端分页逻辑,全场景复用无需单独配置
如果不想让控制器处理JS渲染,可以写一套通用的前端分页逻辑,放在全局JS文件中所有分页场景通用,不需要为每个分页场景新建模板:
- 控制器改造,远程请求返回JSON格式的渲染结果:
# app/controllers/page_controller.rb def home if "default" == @tab_id || "PostForYou" == @tab_id @buffers = Post.where("created_at >= ?", 1.week.ago.utc) .sort_by { |post| cal_post_hot_point(post) } .reverse @posts = Kaminari.paginate_array(@buffers).page(params[:page]).per(10) end respond_to do |format| format.html format.json do render json: { posts_html: render_to_string(@posts, layout: false), next_page_link: @posts.last_page? ? nil : link_to_next_page(@posts, 'Next', remote: true, class: 'universal-next-btn') } end end end
- 首页模板改造,删除原有自定义JS逻辑,使用通用类标记:
<% if @posts.present? %> <!-- 用通用类标记帖子容器,data属性指定对应分页容器 --> <div id="home_infinite_scroll" class="universal-post-container"> <%= render @posts %> </div> <!-- 用通用类标记分页容器 --> <div class="home_pagination_button universal-pagination" data-post-container="#home_infinite_scroll"> <%= link_to_next_page(@posts, 'Next', remote: true, class: 'universal-next-btn') %> </div> <% end %>
- 全局JS(如
application.js)中添加通用分页逻辑,所有场景复用:
$(document).on('click', '.universal-next-btn', function(e) { e.preventDefault(); const $btn = $(this); const $pagination = $btn.closest('.universal-pagination'); const $postContainer = $($pagination.data('post-container')); $.get($btn.attr('href'), { format: 'json' }, function(res) { // 追加新帖子 $postContainer.append(res.posts_html); // 更新分页按钮 if (res.next_page_link) { $pagination.html(res.next_page_link); } else { $pagination.html("<a>End</a>"); } }); });
该方案的优势是逻辑只需写一次,后续其他模型的分页场景只需添加对应的通用类标记即可使用,无需新增任何JS模板或自定义逻辑。
内容的提问来源于stack exchange,提问作者Dung Nguyen
相关产品推荐
相关产品推荐

