Rails - Combine search, pagination and export in Rails

Let’s see how combine the search, pagination and export options in Rails.

For the pagination, I’ve decided to use the kaminari gem. It provides a very easy way to add pagination into Rails. I’ve used it over will_paginate as kaminari includes (for free) some methods for the view pages. It also has the posibility of use some themes to the pagination component displayed in the view.

Regarding the search and export, it will be done using ruby code.

Let’s see the code!

NOTE: Update to use model params!!Better than extract the params inside the model method.

In the view: app/views/bits/index.html.erb

# search component
<%= form_tag({}, method: :get) do %>
  <%= text_field_tag(:path, params[:path]) %>
  <%= submit_tag 'Search' %>
<% end %>

<% @bits.each do |bit| %>
# do whatever you need here with the data
 ...
<% end %>

# pagination
<div>
  <%= paginate @bits %>
</div>

# export
<div id="export">
  <button><%= link_to("Export results in csv", bits_path(path: controller.params["path"], format: :csv)) %></button>
  <button><%= link_to("Export results in xls", bits_path(path: controller.params["path"], format: :xls)) %></button>
</div>

Note: Mention the form_tag has two parameters:

  1. path
  2. method used. Eg: :get

If you indicate the path (eg: form_tag(bits_path, method: :get)), you will get redirected to the bits_path in the model. In case you don’t indicate any path (eg: form_tag({}, method: :get)), you are redirected to the same same path you currently are.

In the controller: app/controllers/bits_controller.rb

def index
  @bits = Bit.search(bits_params)

  respond_to do |format|
    format.html { render :index }
    format.xls { send_data generate_xls(@bits), filename: "Export-#{Date.today}.xls" }
    format.csv { send_data generate_csv(@bits), filename: "Export-#{Date.today}.csv" }
  end
end

private
# define the params permited
def bits_params
  params.permit(:path, :page, :format).to_h.symbolize_keys
end

Where the methods generate_xls and generate_csv are defined as private methods in the controller.

In the model we define the method search(): app/models/bit.rb

def self.search(path: nil, page: nil, format: nil)
  search_query = if path
    where("path LIKE ? ", "%#{path}%")
  else
    all
  end

  if format
    # export
    search_query.find_in_batches
  else
    # display it with pagination
    search_query.page(page)
  end
end