Pagination in Ruby On Rails using will_paginate plugin


We do pagination in Rails by using its built in paginate method in our controller


def display_data
@display_data_pages, @display_data = paginate(:location_data,
:conditions => ["loc.updated_at < ?", params[:date]],
:joins => "as loc inner join location_address as locadd on loc.id = locadd.id"
:per_page => 10,
:order => "location_name")
end

Often we would like to put this code inside our model mainly for two reasons:
1) Code Reuse, and
2) To keep the business logic where it belongs
But we couldn’t do that (since it is difficult to do) due to the paginate method and had to write the same code more than once in the controller and sometimes in more than one controller.
On top of that we have to put this chunk of code in our views only to show the pagination links
<div class="pagination">
<%= if @survdefault_pages.current.previous
link_to("< Prev", { :page => @survdefault_pages.current.previous })
end %>


<%= pagination_links(@survdefault_pages) %>

<%= if @survdefault_pages.current.next
link_to("Next >", { :page => @survdefault_pages.current.next})
end %>
</div>

We can put this code inside a partial but it is still a pain.

Moreover, Rails’ built in pagination will be out of Rails before it will reach version 2 which is not that far.

Update: Rails built in pagination is now removed but is still available as a plugin, ie, classic_pagination.

We have another option, use will_paginate plugin. The advantages of will_paginate plugin are:
1) Easy to use,
2) Less code in views to show pagination links, and
3) It can be used inside models

Follow these steps to use will_paginate plugin

1) Grab it, by going to the command prompt and change directory to the app root. Then run
ruby script/plugin install svn://errtheblog.com/svn/plugins/will_paginate
NOTE If the above code doesn’t installs the plugin in your vendor/plugins directory then you probably don’t have svn installed on your machine.

2) Now in our controller we can simply write

def display_data
@display_data = LocationData.paginate(:page => params[:page],
:conditions => ["loc.updated_at < ?", params[:date]],
:joins => "as loc inner join location_address as locadd on loc.id = locadd.id", :per_page => 10,
:order => "location_name")
end

:per_page and :page are required.
The best thing is, as mentioned above, we can move this code to the LocationData model that will help in keeping our controller clean and will also help in code reuse.
To do that, we can create a class method in our model like:

def self.display_location_data(update_date, page)
paginate(:page => page, :conditions => ["loc.updated_at < ?", update_date], :joins => "as loc inner join location_address as locadd on loc.id = locadd.id",
:per_page => 10,
:order => "location_name")
end

In our controller, the display_data method will now be replaced with just one line

def display_data
@display_data = LocationData.display_location_data(params[:date], params[:page])
end

In the view, the big chunk of code will be replaced by a single line to show our pagination links, like

<%= will_paginate @display_data %>

And we are done…

UPDATE: Formatting / Styling pagination links

Advertisements

46 thoughts on “Pagination in Ruby On Rails using will_paginate plugin

  1. Balachandran says:

    Dear sir,
    How to do the pagination using joining tables as well as joining tables.
    For example:
    I have 5 tables.
    1st query joining with 3 tables and assigned one variable
    2nd query joining with 3 tables and assigned one variable
    3rd query joining with 3 tables and assigned one variable
    Finally i was concatenate those three queries and assigned one variables.
    so finally i want to do pagination. i dont know how to do??
    Can u reply me.??
    Regards
    bala

  2. nasir says:

    hi Bala
    Before I answer your questions, I need to know why you want to do something like this:
    “1st query joining with 3 tables and assigned one variable
    2nd query joining with 3 tables and assigned one variable
    3rd query joining with 3 tables and assigned one variable”

    Why can’t you just get everything in one join query instead of three? OR Do you really need to show all that data on every page?

    To me it looks more like a design problem rather than a pagination issue.

    I would love to help but can you describe it in a bit more detail why you want to do something like this and the problem you are trying to solve.

    Cheers

    Nasir

  3. nasir says:

    You don’t have to do any thing special for that in

    <%= will_paginate @obj_instance %>

    just provide the link normally like so:

    <%= link_to('A link', :controller => :jobs, :action => :index, :skill_type => 'ruby') %>

    or if you are app is RESTful then

    <%= link_to('A link', jobs_path(:skill_type => 'ruby')) %>

    If you have a page at http://www.example.com/jobs that uses pagination and can be accessed at http://www.example.com/jobs?page=2, http://www.example.com/jobs?page=3, etc then your skill_type parameter will be added in the URL, for instance, http://www.example.com/jobs?page=2&skill_type=ruby That is, all your pagination urls will have an additional parameter of skill_type and its value attached to it.

  4. sijo says:

    Hi
    I have a search UI.And I use will_paginate to paginate result.My action
    is
    def search_sd_ticket
    #@search_sd_ui_hash=params[:sd_ticket]
    @search_sd_ticket_result=ServiceDeskTicket.record_paginate_sd(params[:sd_ticket],params[:page])

    end

    and in ServiceDeskTicket model

    def self.record_paginate_sd(search_sd_ui_hash,page)
    def self.record_paginate_sd(search_sd_ui_hash,page)
    paginate(:page => page, :conditions => [“number LIKE ?”,
    “%#{sd_ticket_number}%”],
    :per_page => 10,
    :order => “number”)

    end

    My problem is the first page comes but when i click on the next page the
    following error happens
    You have a nil object when you didn’t expect it!
    You might have expected an instance of Array.
    The error occurred while evaluating nil.[]

    The second time params[:sd_ticket] has no value..ow can I solve
    this?Please help

    Sijo

  5. nasir says:

    Sijo

    The problem is in the class method of your model here
    :conditions => [“number LIKE ?”, “%#{sd_ticket_number}%”]

    if you change sd_ticket_number to search_sd_ui_hash then i think it should work fine because i cant see you getting a value for sd_ticket_number from anywhere though you are passing search_sd_ui_hash parameter to your class method in the model

  6. Niksan says:

    Hi Nasir!

    I have used this pagination on one of the pages. I want to edit the styles for only the Previous or Next links. The current styles define a Link in a general way and have 2 styles ‘current’ and ‘disabled’ only for the two .

    Is there a way to apply a separate css class to Previous and Next links?

    Awaiting your reply.

    Thanks in advance,
    Nik

  7. nasir says:

    Nik

    Yes you can. In your view, instead of doing
    will_paginate object
    do this

    will_paginate(object, :next_label => "<span class='class_name'>Next label</span>", :prev_label => "<span class='class_name'>Previous Label</span>" )

    This will add css classes before your previous and next label.

  8. lucian says:

    Is it possible to pass a :layout =>false to the pagination. I have the pagination in a tab and a simple will_paginate messes up the display

  9. Jam says:

    Hi Nasir!

    I’ve been trying to use will_paginate but I get this error:

    undefined method `total_pages’

    In my model I have:

    def self.paginate_find_functions(project_module_id_params, page)
    self.paginate(:all, :page =>page, :conditions => { :project_module_id => project_module_id_params }, :per_page => 1, :order => “name”)
    end

    In my controller I have:

    @functions = Function.paginate_find_functions(params[:project_module_id], params[:page])

    In my view I have:

    Please help me :)!

  10. nasir says:

    Jam,

    I don’t think there is anything wrong with your code and assume that in your view you have

    <%= will_paginate @functions %>

    Suggest you either uninstall/reinstall will_paginate plugin or please paste the full error you are getting.

    Cheers

  11. nasir says:

    if your app is restful then in your routes file add:

    map.resources :your_resource_name, :path_prefix => “page/:page”

    if you are using named routes then use:

    map.your_route_name ‘page/:page/your_route_name’, :controller => ‘cname’, :action => ‘aname’,

    if you are not using any of the above then use:

    map.connect ‘:controller/:action/page/:page’, :controller => ‘controller_name’, :action => ‘action_name’

  12. Eric says:

    Hi Nasir,

    Are you sure this works for restful apps:
    map.resources :your_resource_name, :path_prefix => “page/:page”

    I tried this but without succes.

    Which rails version and which version of will_paginate do you use?

  13. nasir says:

    You need to take a couple of things into consideration:
    1. your actual url will be domain.com/page/1/resource/ and not domain.com/resource/page/1
    2. if you add path_prefix option in your route file for a resource, for instance, employees in a nested route with url like domain.com/organisation/department/employees, then your url will map to domain.com/page/1/employees instead of domain.com/organisation/department/employees?page=1

    so whether it works or not with restful routes depends upon what you want to achieve.

  14. Yukti says:

    Hello Nasir
    Hope you doing fine. I have been going through articles and have been great help.Esp the will_paginate. My paginations works perfect.But thers an issue with its combination with the “serach” feature , I am working for.
    In the controller, I have added an action “manage_list_actions ” feature to search records for the given search keys. After the records fetch as
    @employees= Employee.paginate(:all , :page => params[:page] , :conditions=>[“department_id=?”,”#{@search_dep_id}”] , :per_page =>2 , :order => sort_clause)

    I am rendering to render:action => “index” . and my problem starts 🙂
    there after , pagination links when cliecked take me to /employees/manage_list_acitons?page=2.
    and I get the error “Couldn’t find Employee with ID=manage_list_actions”. I am understanding that its taking action in place of Id . So some problem in either in rendering OR routes.
    Please help.

    Yukti.Vig

  15. nasir says:

    Yup, that’s a route issue. In your routes.rb file, change the line

    map.resources :employees

    to

    map.resources :employees, :collection => {:manage_list_actions => :get}

    (replace :get with :post if you are posting a form to this page)

    This should sort your issue.

  16. parimala says:

    Dear sir,

    i want pagination in this format using ruby on rails
    by using will_paginate plugin……please help me out in this problem

    regards, pari

  17. Sandeep says:

    Failure Alert –
    Am a newbie to Ruby on Rails and have been trying to get setup using Komodo IDE. They have a tutorial at http://docs.activestate.com/komodo/5.0/tutorial/railstut.html which talks about using this plugin. will_paginate.
    The doc is obsolete.
    Even the instructions at http://github.com/mislav/will_paginate/wikis/installation are obsolete!

    Only 1 method out of the 4 works – to install as a gem.

    The methods to use as a plugin or use git do not work. SVN is documented as not being supported and the URL has been taken down.

    Hope this saves someone the number of hours I spent in finding this out!
    -Sandeep

  18. shridevi says:

    Hi,

    Could you please tell whether i can use method post with pagination using will_paginate,if so how to do that.
    AS of now if i use method post then pagination is not working in the sense
    when i click on page 2 i get errors as all the parameters are not submitted

  19. Zeba says:

    Hi Nasir,
    Can you please tell me how do I paginate results (in the form of arrays) which are already fetched ????
    I’ve tried paginate_by_sql, paginate_from_sql, paginate_collection, but none of them seem to work for me… :((
    Please help..
    Thanks….

  20. Zeba says:

    Hey…Nasir
    Thanks a lot for your help…
    I have my pagination working now….as I’m using the paginating_find plugin with the paginating_sql_find function which takes sql queries as input…!!
    Anyways…thanks for your help.. 🙂

  21. zimbo says:

    just added pagination to my site and had the same prob as Yukti. my site is using partials for the search result. according to the rails docu i have render :partial=>'search_result' , :layout=>false this works fine with without pagination, but with it the whole navigation disappears i.e. only the result table shown. any suggestions?

    routes.rb

    map.resources :XXX,:collection =>{:get_not_complete=>:get,:get_with_bb=>:get,:custom_method=>:get},:member =>{:copy_something=>:any}

  22. S.Janardhanan says:

    Hi i am new to rails..
    i followed ur steps..
    now how can i check whether the plug in has been install??
    thanks in advance.

  23. m.sasibhushan says:

    i am getting undefined method ‘total pages’ when i am giving i don’t know why it is happening .
    and in controller i am trying to give as:

    @comments = Comment.paginate(:page => params[:page])

    i tried this giving in index and show page but i am getting the same error .

  24. quynguyenNguyen says:

    Hi sasibhushan,

    I have the same error, and fixed it with such a funny solution:

    @comments = Comment.paginate(:page => params[:page])
    @comments = @comments.paginate(:page => params[:page])

    Try this 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s