We all know that rendering all the data at once hampers the application’s performance and for that purpose we use pagination so that we can render sets of data with each page. But sometimes for a better user experience, wouldn’t it be a good idea that once the user is finished with a set of data the next set of data is rendered and appended after that, rather that waiting for the user to click the next link for the remaining data and so this is were we implement endless page.
Below is the code that i have used for implementing endless page in my application using jquery and the will_paginate plugin.
1. First make sure that you have will_paginate gem installed.
gem 'will_paginate', '~> 3.0' (Include this gem inside your gemfile)
and run bundle command to install this gem.
2. Inside the Controller
def index
per_page = 40
page = params[:page]
@sorting = sorting?
if @sorting
per_page *= (page || 1).to_i
page = 1
end
@users = User.paginate :page => page,
:per_page => per_page,
:include => [:profile],
:order => sort_order.blank? ? 'created_at desc' : sort_order
end
Make sure to call the paginate method
3. The Javascript
jQuery(function() {
$('#loading').hide();
if ($('.pagination').length) {
$('#endless_paginate').prepend('Show more');
return $('#add_results').click(function() {
var url;
url = $('.pagination .next_page').attr('href');
$('#endless_paginate').show();
if (url) {
$('#endless_paginate').hide();
$('#loading').show();
return $.getScript(url);
}
});
}
});
Here we check first that if we have a pagination element present , then we prepend the endless_paginate div with a Show more link. Then we declare a url that will have the pagination element and we call the next page element and fetch the url attribute. This pagination element is available to us through the will_paginate gem. Next if the url is present then we would fetch more results with the getScript method to which we would pass the url attribute that would fire the index action and return javascript once executed.
4. Inside index.js.erb
$('#list_header').html("<%= escape_javascript render("list_headers") %>");
$("#list_of_users table tbody")
.append("<%= escape_javascript render("list") %>");
$('.pagination').replaceWith
("<%= escape_javascript will_paginate(@users) %>");
$('#loading').hide();
if($('.pagination').find('span.disabled').length > 0){
$('#endless_paginate').hide();
}
else{
$('#endless_paginate').show();
}
$('#pagenone').html("<%= escape_javascript will_paginate(@users) %>");
Here inside the index.js we would append the (list_of_users) div with the rendered list partial that renders the @users. secondly we have to make sure to update the pagination link which we can do with calling the replaceWith on the pagination element and replace it with the will_paginate and pass it @users.
The rest of code is responsible for hiding and showing elements.
5. Changes in the views
Inside index.html.haml
#list_of_users
%table{:cellpadding => "0", :cellspacing => "0"
, :width => "100%", :id => "users"}
%thead#list_header
= render 'list_headers'
%tbody#list
= render 'list'
#loading{:class => 'paddleft'}
%img{:src => "/images/loading.gif"}/
#endless_paginate{:class => 'appendpaginate'}
#pagenone{:class => 'nopagination'}
= will_paginate @users
Inside _list_headers.html.haml
%tr#list_header
%th Image
%th Screen Name
%th Name
%th Last Login
%th Site
%th Logins
Inside _list.html.haml
= render @users
Inside _user.html.haml
%tr{:id => "user_#{user.id}"}
%td
- if !user.photos.last.nil?
= link_to (image_tag(user.photos.last.photo.thumb.url,
:alt => 'No Image', :class => "sizing"),
user.photos.last.photo.large.url , :class => "preview")
- else
= image_tag("/images/no_image.jpg", :style => "sizing")
%td= user.profile.screen_name
%td
%span.working
= link_to user.full_name, edit_user_path(user)
%td= user.last_sign_in_at ? format_date(user.last_sign_in_at) : "N/A"
%td= user.site.name
%td.logins.center= user.sign_in_count.nil? ? 0 : user.sign_in_count