Ruby on Rails
PaginationHelper (Version #77)

ActionPack PaginationHelper for ActiveRecord collections

The built-in pagination is being DEPRECATED. Look into the will_paginate plugin.

The Pagination module aids in the process of paging large collections of Active Record objects. It offers macro-style automatic fetching of your model for multiple views, or explicit fetching for single actions. And if the magic isn’t flexible enough for your needs, you can create your own paginators with a minimal amount of code.

The Pagination module can handle as much or as little as you wish. In the controller, have it automatically query your model for pagination; or, if you prefer, create Paginator objects yourself.

Pagination is included automatically for all controllers.

Controller examples

PaginationHelper can handle as much or as little as you wish. In the controller, have it automatically query your model for pagination; or, if you prefer, create Paginator objects yourself.

Automatic pagination for every action in a controller

  class PersonController < ApplicationController
    paginate :people, :order_by => 'last_name, first_name',
             :per_page => 20

    # ...
  end

Each action in this controller now has access to a @people instance variable, which is an ordered collection of model objects for the current page (at most 20, sorted by last name and first name), and a @person_pages Paginator instance. The current page is determined by the params[‘page’] variable.

Pagination for a single action

  def list
    @person_pages, @people =
      paginate :people, :order_by => 'last_name, first_name'
  end

Like the previous example, but explicitly creates @person_pages and @people for a single action, and uses the default of 10 items per page.

Custom/”classic” pagination

  def list
    @person_pages = Paginator.new self, People.count, 10, params['page']
    @person = People.find_all nil, 'last_name, first_name',
              @person_pages.current.to_sql
  end

Explicitly creates the paginator from the previous example and uses Paginator#to_sql to retrieve @people from the model.

View examples

Paginator Helper includes various methods to help you display pagination links in your views. (For information on displaying the paginated collections you retrieve in the controller, see the documentation for ActionView::Partials#render_partial_collection.)

Using Paginator#pagination_links

Use the basic_html method to get a simple list of pages (always including the first and last page) with a window around the current page. In your view, using one of the controller examples above,

  <%= pagination_links(@person_pages) %>

will render a list of links. For a list of parameters, see the documentation for Page#pagination_links.

Using a paginator partial

If you need more advanced control over pagination links and need to paginate multiple actions and controllers, consider using a shared paginator partial. For instance, _why suggests this partial, which you might place in app/views/partial/_paginator.rhtml:

 <div class="counter">
   Displaying <%= paginator.current.first_item %>
   - <%= paginator.current.last_item %>
   of <%= paginator.item_count %>    

   <%= link_to(h('< Previous'), {:page => paginator.current.previous})  + " | " if paginator.current.previous %>
   <%=pagination_links(paginator, :window_size => 4) %>
   <%= " | " + link_to(h('Next >'), {:page => paginator.current.next}) if paginator.current.next %>
 </div>

Then in your views, simply call

  <%= render :partial => "partial/paginator", :locals => { :paginator => @person_pages } %>

wherever you want your pagination links to appear.

If you have search parameters that you need to include in the links to various pages you can use the following modified version of the above partial:


<div class="counter">
   Displaying <%= paginator.current.first_item %>
   - <%= paginator.current.last_item %>
   of <%= paginator.item_count %>    

   <%  search_params = params[:search].collect {|k,v| ["search[#{k.to_s}]", v]}.flatten.to_h %>

   <%= link_to('&laquo; Previous', search_params.merge({:page => paginator.current.previous}))  + " | " if paginator.current.previous %>
   <%= pagination_links(paginator, :window_size => 4, :params => search_params) %>
   <%= " | " + link_to('Next &raquo;', search_params.merge({:page => paginator.current.next})) if paginator.current.next %>
 </div>

to_h doesn’t seem to be valid. Here’s an alternative:


<% search_params = Hash[*params[:search].collect {|k,v| ["search[#{k.to_s}]", v]}.flatten] %>

question: this code didn’t work for me, probably because of the use of params.

I replaced the search_params initialization by


    <% search_params = request.parameters.delete_if { |key,value| [:action, :controller].include? key} %>

(wonder why request.query_parameters doesn’t exist.. It does but return a String)

Otherwise this is what I get:


undefined local variable or method `paginator' for #<#<Class:0xb74b31bc>:0xb74b316c>

[...]

vendor/rails/actionpack/lib/action_view/base.rb:316:in `compile_and_render_template'
vendor/rails/actionpack/lib/action_view/base.rb:292:in `render_template'
vendor/rails/actionpack/lib/action_view/base.rb:251:in `render_file'
vendor/rails/actionpack/lib/action_view/base.rb:266:in `render'
vendor/rails/actionpack/lib/action_view/partials.rb:59:in `render_partial'

Paginate a collection

Sometimes it’s nearly impossible to paginate a result set using the built-in :limit and :offset parameters of find(:all). Instead, you can fetch a complicated query and paginate the results afterward.

If you need to paginate a collection, add this method to your application.rb:


 def paginate_collection(collection, options = {})
    default_options = {:per_page => 10, :page => 1}
    options = default_options.merge options

    pages = Paginator.new self, collection.size, options[:per_page], options[:page]
    first = pages.current.offset
    last = [first + options[:per_page], collection.size].min
    slice = collection[first...last]
    return [pages, slice]
  end

and call it from an action like this:

 <div class="counter">
@pages, @users = paginate_collection User.find_custom_query, :page => params[:page]
 </div>

—-
In paganation_links, is there any way to put the current page in a <span> element rather than just out there on it’s own?

ActionPack PaginationHelper for ActiveRecord collections

The built-in pagination is being DEPRECATED. Look into the will_paginate plugin.

The Pagination module aids in the process of paging large collections of Active Record objects. It offers macro-style automatic fetching of your model for multiple views, or explicit fetching for single actions. And if the magic isn’t flexible enough for your needs, you can create your own paginators with a minimal amount of code.

The Pagination module can handle as much or as little as you wish. In the controller, have it automatically query your model for pagination; or, if you prefer, create Paginator objects yourself.

Pagination is included automatically for all controllers.

Controller examples

PaginationHelper can handle as much or as little as you wish. In the controller, have it automatically query your model for pagination; or, if you prefer, create Paginator objects yourself.

Automatic pagination for every action in a controller

  class PersonController < ApplicationController
    paginate :people, :order_by => 'last_name, first_name',
             :per_page => 20

    # ...
  end

Each action in this controller now has access to a @people instance variable, which is an ordered collection of model objects for the current page (at most 20, sorted by last name and first name), and a @person_pages Paginator instance. The current page is determined by the params[‘page’] variable.

Pagination for a single action

  def list
    @person_pages, @people =
      paginate :people, :order_by => 'last_name, first_name'
  end

Like the previous example, but explicitly creates @person_pages and @people for a single action, and uses the default of 10 items per page.

Custom/”classic” pagination

  def list
    @person_pages = Paginator.new self, People.count, 10, params['page']
    @person = People.find_all nil, 'last_name, first_name',
              @person_pages.current.to_sql
  end

Explicitly creates the paginator from the previous example and uses Paginator#to_sql to retrieve @people from the model.

View examples

Paginator Helper includes various methods to help you display pagination links in your views. (For information on displaying the paginated collections you retrieve in the controller, see the documentation for ActionView::Partials#render_partial_collection.)

Using Paginator#pagination_links

Use the basic_html method to get a simple list of pages (always including the first and last page) with a window around the current page. In your view, using one of the controller examples above,

  <%= pagination_links(@person_pages) %>

will render a list of links. For a list of parameters, see the documentation for Page#pagination_links.

Using a paginator partial

If you need more advanced control over pagination links and need to paginate multiple actions and controllers, consider using a shared paginator partial. For instance, _why suggests this partial, which you might place in app/views/partial/_paginator.rhtml:

 <div class="counter">
   Displaying <%= paginator.current.first_item %>
   - <%= paginator.current.last_item %>
   of <%= paginator.item_count %>    

   <%= link_to(h('< Previous'), {:page => paginator.current.previous})  + " | " if paginator.current.previous %>
   <%=pagination_links(paginator, :window_size => 4) %>
   <%= " | " + link_to(h('Next >'), {:page => paginator.current.next}) if paginator.current.next %>
 </div>

Then in your views, simply call

  <%= render :partial => "partial/paginator", :locals => { :paginator => @person_pages } %>

wherever you want your pagination links to appear.

If you have search parameters that you need to include in the links to various pages you can use the following modified version of the above partial:


<div class="counter">
   Displaying <%= paginator.current.first_item %>
   - <%= paginator.current.last_item %>
   of <%= paginator.item_count %>    

   <%  search_params = params[:search].collect {|k,v| ["search[#{k.to_s}]", v]}.flatten.to_h %>

   <%= link_to('&laquo; Previous', search_params.merge({:page => paginator.current.previous}))  + " | " if paginator.current.previous %>
   <%= pagination_links(paginator, :window_size => 4, :params => search_params) %>
   <%= " | " + link_to('Next &raquo;', search_params.merge({:page => paginator.current.next})) if paginator.current.next %>
 </div>

to_h doesn’t seem to be valid. Here’s an alternative:


<% search_params = Hash[*params[:search].collect {|k,v| ["search[#{k.to_s}]", v]}.flatten] %>

question: this code didn’t work for me, probably because of the use of params.

I replaced the search_params initialization by


    <% search_params = request.parameters.delete_if { |key,value| [:action, :controller].include? key} %>

(wonder why request.query_parameters doesn’t exist.. It does but return a String)

Otherwise this is what I get:


undefined local variable or method `paginator' for #<#<Class:0xb74b31bc>:0xb74b316c>

[...]

vendor/rails/actionpack/lib/action_view/base.rb:316:in `compile_and_render_template'
vendor/rails/actionpack/lib/action_view/base.rb:292:in `render_template'
vendor/rails/actionpack/lib/action_view/base.rb:251:in `render_file'
vendor/rails/actionpack/lib/action_view/base.rb:266:in `render'
vendor/rails/actionpack/lib/action_view/partials.rb:59:in `render_partial'

Paginate a collection

Sometimes it’s nearly impossible to paginate a result set using the built-in :limit and :offset parameters of find(:all). Instead, you can fetch a complicated query and paginate the results afterward.

If you need to paginate a collection, add this method to your application.rb:


 def paginate_collection(collection, options = {})
    default_options = {:per_page => 10, :page => 1}
    options = default_options.merge options

    pages = Paginator.new self, collection.size, options[:per_page], options[:page]
    first = pages.current.offset
    last = [first + options[:per_page], collection.size].min
    slice = collection[first...last]
    return [pages, slice]
  end

and call it from an action like this:

 <div class="counter">
@pages, @users = paginate_collection User.find_custom_query, :page => params[:page]
 </div>

—-
In paganation_links, is there any way to put the current page in a <span> element rather than just out there on it’s own?