Will Paginate in Rails 5

Steps

You can download the source code for this article from wpag.

Step 1

Add the gem to the Gemfile.

gem 'will_paginate', '>= 3.1'

Run:

bundle

Step 2

def index
  @tasks = Task.search(params[:term], params[:page])
end

Step 3

Add the will_paginate view helper to the tasks index page.

<%= will_paginate @tasks %>

Step 4

You can play in the Rails console to see how the will_paginate works:

 > t = Task.paginate(page: 1, per_page: 5)
  Task Load (1.6ms)  SELECT  "tasks".* FROM "tasks" LIMIT ? OFFSET ?  [["LIMIT", 5], ["OFFSET", 0]]
 => #<ActiveRecord::Relation [#<Task id: 1, name: "Get rich quick", complete: true, created_at: "2016-03-18 21:37:57", updated_at: "2016-03-24 01:12:25", project_id: 1, priority: 4, due_date: "2016-01-18 21:37:57">, #<Task id: 2, name: "Write a book", complete: true, created_at: "2016-03-18 21:37:57", updated_at: "2016-03-18 21:37:57", project_id: 1, priority: 5, due_date: "2016-02-18 21:37:57">, #<Task id: 3, name: "Buy a puppy", complete: false, created_at: "2016-03-18 21:37:57", updated_at: "2016-03-18 21:37:57", project_id: 2, priority: 9, due_date: "2016-02-18 21:37:57">, #<Task id: 4, name: "Dance in the rain", complete: true, created_at: "2016-03-18 21:37:57", updated_at: "2016-03-18 22:20:05", project_id: 2, priority: 7, due_date: "2016-01-22 21:37:57">, #<Task id: 5, name: "Dance in the snow", complete: true, created_at: "2016-03-18 21:37:57", updated_at: "2016-03-18 21:37:57", project_id: 2, priority: 6, due_date: "2016-01-18 21:37:57">]> 
 > t.class
 => Task::ActiveRecord_Relation 

We can call the paginate method with current page and per page values. It returns ActiveRelation object. We can get all the records by calling to_a on ActiveRelation object.

 > results = t.to_a
   (0.2ms)  SELECT COUNT(*) FROM "tasks"
 => [#<Task id: 1, name: "Get rich quick", complete: true, created_at: "2016-03-18 21:37:57", updated_at: "2016-03-24 01:12:25", project_id: 1, priority: 4, due_date: "2016-01-18 21:37:57">, #<Task id: 2, name: "Write a book", complete: true, created_at: "2016-03-18 21:37:57", updated_at: "2016-03-18 21:37:57", project_id: 1, priority: 5, due_date: "2016-02-18 21:37:57">, #<Task id: 3, name: "Buy a puppy", complete: false, created_at: "2016-03-18 21:37:57", updated_at: "2016-03-18 21:37:57", project_id: 2, priority: 9, due_date: "2016-02-18 21:37:57">, #<Task id: 4, name: "Dance in the rain", complete: true, created_at: "2016-03-18 21:37:57", updated_at: "2016-03-18 22:20:05", project_id: 2, priority: 7, due_date: "2016-01-22 21:37:57">, #<Task id: 5, name: "Dance in the snow", complete: true, created_at: "2016-03-18 21:37:57", updated_at: "2016-03-18 21:37:57", project_id: 2, priority: 6, due_date: "2016-01-18 21:37:57">] 
 > results.size
 => 5 

There are 10 tasks.

 > Task.count
   (0.1ms)  SELECT COUNT(*) FROM "tasks"
 => 10 

We can use the page method provided by will_paginate to paginate the tasks.

 > t1 = Task.page(1).order('id DESC')
  Task Load (0.4ms)  SELECT  "tasks".* FROM "tasks" ORDER BY id DESC LIMIT ? OFFSET ?  [["LIMIT", 30], ["OFFSET", 0]]
 => #<ActiveRecord::Relation [#<Task id: 10, name: "Walk on moon", complete: true, created_at: "2016-03-18 21:37:57", updated_at: "2016-03-18 21:37:57", project_id: 2, priority: 2, due_date: "2016-03-16 21:37:57">, #<Task id: 9, name: "Make fun of Donald Trump", complete: true, created_at: "2016-03-18 21:37:57", updated_at: "2016-03-18 21:37:57", project_id: 2, priority: 3, due_date: "2016-03-16 ...">]> 
 > t1.class
 => Task::ActiveRecord_Relation 
 > t1r = t1.to_a
 => [#<Task id: 10, name: "Walk on moon", complete: true, created_at: "2016-03-18 21:37:57", updated_at: "2016-03-18 21:37:57", project_id: 2, priority: 2, due_date: "2016-03-16 21:37:57">, #<Task id: 9, name: "Make fun of Donald Trump", complete: true, created_at: "2016-03-18 21:37:57", updated_at: "2016-03-18 21:37:57", project_id: 2, priority: 3, due_date: "2016-03-16 21:37:57">, #<Task id: 8, name: "Play tennis", complete: true, created_at: "2016-03-18 21:37:57", updated_at: "2016-03-18 21:37:57", project_id: 2, priority: 4,...>] 
 > t1r.size
 => 10 

Step 5

Let's use the page method provided by the will_paginate gem in the task model.

def self.search(term, current_page)
  if term
    page(current_page).where('name LIKE ?', "%#{term}%").order('id DESC')
  else
    page(current_page).order('id DESC') 
  end
end

This gives 10 tasks per page. So no pagination links are displayed on the browser. Because we only have 10 tasks.

Step 6

Let's use the paginate method provided by will_paginate gem in the task model.

def self.search(term, page)
  if term
    where('name LIKE ?', "%#{term}%").paginate(page: page, per_page: 5).order('id DESC')
  else
    paginate(page: page, per_page: 5).order('id DESC') 
  end
end

The pagination now works for index and search results.

Step 7

The tasks index page looks like this:

<h1>Tasks</h1>
<%= form_tag(tasks_path, method: :get) do %>
   <%= text_field_tag :term, params[:term] %>
   <%= submit_tag 'Search', name: nil %>
<% end %>
<table>
  <thead>
    <tr><th></th></tr>
  </thead>
  <tbody>
    <tr>
      <% for task in @tasks %>
        <div class='task'>
            <strong> <%= task.name %> </strong>
        </div>
        <%= link_to 'Edit', edit_task_path(task) %>
        <%= link_to 'Show', task %>
      <% end %>
     </tr>       
  </tbody>
</table>

<%= will_paginate @tasks %>

Step 8

You can customize the pagination look and feel. Copy the following CSS from will_paginate wiki to application.css.

.digg_pagination {
  background: white;
  cursor: default;
  /* self-clearing method: */ }
  .digg_pagination a, .digg_pagination span, .digg_pagination em {
    padding: 0.2em 0.5em;
    display: block;
    float: left;
    margin-right: 1px; }
  .digg_pagination .disabled {
    color: #999999;
    border: 1px solid #dddddd; }
  .digg_pagination .current {
    font-style: normal;
    font-weight: bold;
    background: #2e6ab1;
    color: white;
    border: 1px solid #2e6ab1; }
  .digg_pagination a {
    text-decoration: none;
    color: #105cb6;
    border: 1px solid #9aafe5; }
    .digg_pagination a:hover, .digg_pagination a:focus {
      color: #000033;
      border-color: #000033; }
  .digg_pagination .page_info {
    background: #2e6ab1;
    color: white;
    padding: 0.4em 0.6em;
    width: 22em;
    margin-bottom: 0.3em;
    text-align: center; }
    .digg_pagination .page_info b {
      color: #000033;
      background: #6aa6ed;
      padding: 0.1em 0.25em; }
  .digg_pagination:after {
    content: ".";
    display: block;
    height: 0;
    clear: both;
    visibility: hidden; }
  * html .digg_pagination {
    height: 1%; }
  *:first-child + html .digg_pagination {
    overflow: hidden; }

.apple_pagination {
  background: #f1f1f1;
  border: 1px solid #e5e5e5;
  text-align: center;
  padding: 1em;
  cursor: default; }
  .apple_pagination a, .apple_pagination span {
    padding: 0.2em 0.3em; }
  .apple_pagination .disabled {
    color: #aaaaaa; }
  .apple_pagination .current {
    font-style: normal;
    font-weight: bold;
    background-color: #bebebe;
    display: inline-block;
    width: 1.4em;
    height: 1.4em;
    line-height: 1.5;
    -moz-border-radius: 1em;
    -webkit-border-radius: 1em;
    border-radius: 1em;
    text-shadow: rgba(255, 255, 255, 0.8) 1px 1px 1px; }
  .apple_pagination a {
    text-decoration: none;
    color: black; }
    .apple_pagination a:hover, .apple_pagination a:focus {
      text-decoration: underline; }

.flickr_pagination {
  text-align: center;
  padding: 0.3em;
  cursor: default; }
  .flickr_pagination a, .flickr_pagination span, .flickr_pagination em {
    padding: 0.2em 0.5em; }
  .flickr_pagination .disabled {
    color: #aaaaaa; }
  .flickr_pagination .current {
    font-style: normal;
    font-weight: bold;
    color: #ff0084; }
  .flickr_pagination a {
    border: 1px solid #dddddd;
    color: #0063dc;
    text-decoration: none; }
    .flickr_pagination a:hover, .flickr_pagination a:focus {
      border-color: #003366;
      background: #0063dc;
      color: white; }
  .flickr_pagination .page_info {
    color: #aaaaaa;
    padding-top: 0.8em; }
  .flickr_pagination .previous_page, .flickr_pagination .next_page {
    border-width: 2px; }
  .flickr_pagination .previous_page {
    margin-right: 1em; }
  .flickr_pagination .next_page {
    margin-left: 1em; }

Change the tasks index page as follows:

<div class="apple_pagination">
  <div class="page_info">
    <%= page_entries_info @tasks %>
  </div>
  <%= will_paginate @tasks, :container => false %>
</div>

You can change the look and feel of the pagination by changing the class to the classes defined in the application.css for flickr, apple or digg.

Summary

In this article, you learned how to use will_paginate gem to paginate ActiveRecord objects in Rails 5. You also learned how to customize the look and feel of the pagination.


Related Articles

Watch this Article as Screencast

You can watch this as a screencast Will Paginate in Rails 5