如何用will_paginate gem实现无限滚动?求助排查实现失败问题
Hey there! Let’s get your infinite scroll working with will_paginate. I’ve debugged this exact issue dozens of times, so let’s break down the fixes focusing on your posts_controller.rb and _allposts.html.erb files.
1. First, Double-Check Your Controller Setup
The most common mistake here is not handling AJAX requests properly or misconfiguring pagination. Here’s what your controller action (probably index) should look like:
# app/controllers/posts_controller.rb def index # Order posts (optional but recommended) and paginate with a per-page limit @posts = Post.order(created_at: :desc).paginate(page: params[:page], per_page: 8) # Critical: Respond to both HTML and AJAX (JS) requests respond_to do |format| format.html # Renders the default index.html.erb format.js # Will render index.js.erb for AJAX calls end end
If you skip the respond_to block, your AJAX requests will return full HTML pages instead of the partial content you need for infinite scroll—this is a super common failure point!
2. Update Your _allposts.html.erb Partial
Your partial needs to wrap posts in a targetable container and include a way to trigger the next page (either a "Load More" button or just a hidden marker for auto-scroll). Here’s a solid structure:
<!-- app/views/posts/_allposts.html.erb --> <div id="posts-container"> <% @posts.each do |post| %> <div class="post-item"> <!-- Your existing post content here --> <h3><%= post.title %></h3> <p><%= post.body.truncate(150) %></p> </div> <% end %> </div> <!-- Handle pagination state --> <% if @posts.next_page %> <div id="pagination-trigger"> <%= link_to 'Load More', posts_path(page: @posts.next_page), remote: true, id: "load-more-link" %> </div> <% else %> <p class="no-more-posts">That's all the posts we've got!</p> <% end %>
Key notes here:
- The
#posts-containergives us a clear place to append new posts via JS. - The
remote: trueflag on the link tells Rails to send an AJAX request instead of a full page reload. - We use
@posts.next_pageto get the correct next page number (no manual incrementing—this avoids off-by-one errors).
3. Add the JS to Handle Infinite Scroll
You’ll need a JavaScript view to process the AJAX response. Create index.js.erb in your app/views/posts folder:
// app/views/posts/index.js.erb // Append the new posts to the container $('#posts-container').append('<%= j render partial: "allposts", locals: { posts: @posts } %>'); // Update the "Load More" link or remove it if there are no more pages <% if @posts.next_page %> $('#load-more-link').attr('href', '<%= posts_path(page: @posts.next_page) %>'); <% else %> $('#pagination-trigger').replaceWith('<p class="no-more-posts">No more posts to load!</p>'); <% end %>
The j (alias for escape_javascript) ensures your HTML is properly escaped for JS, preventing syntax errors—don’t skip this!
Want Auto-Scroll Instead of a Button?
If you don’t want a "Load More" button and want to load posts when the user scrolls to the bottom, add this JS to your application.js or a dedicated posts.js file:
$(window).on('scroll', function() { // Check if we're near the bottom of the page if ($(window).scrollTop() + $(window).height() >= $(document).height() - 100) { const loadLink = $('#load-more-link'); // Only request if there's a valid link and we're not already loading if (loadLink.length && !loadLink.hasClass('loading')) { loadLink.addClass('loading'); $.get(loadLink.attr('href'), function(data) { // Append new posts $('#posts-container').append($(data).find('#posts-container').html()); // Update the link or remove it const newLink = $(data).find('#load-more-link'); if (newLink.length) { loadLink.attr('href', newLink.attr('href')).removeClass('loading'); } else { $('#pagination-trigger').replaceWith('<p class="no-more-posts">No more posts!</p>'); } }); } } });
Common Reasons Your Infinite Scroll Failed
Let’s cover the top pitfalls that trip people up:
- Missing
respond_to :js: Without this, Rails returns HTML instead of JS, breaking the partial append. - Incorrect selectors: If your JS targets a container that doesn’t exist (e.g., misspelled
#posts-container), nothing will happen. - Manual page increment: Using
params[:page] + 1instead of@posts.next_pagecan lead to invalid page numbers (e.g., trying to load page 5 when there are only 4 pages). - No
escape_javascript: Forgettingjbeforerendercauses syntax errors in the JS response. - Not handling last page: If you don’t remove the load link when there are no more pages, your app will keep making invalid requests.
内容的提问来源于stack exchange,提问作者keith

