I'm trying to implement pagination in my Rails project using the Pagy gem. I have a Record
Model and index view for displaying my Records
in a element and a form above the table to filter the table which is in a partial that contains the in a Turbo Frame.
My issue is that Pagy initially does paginate the search results (defined by 'records#search') inside the turbo frame but then when I click from one page to another page, it immediately goes to that page as if I am browsing unfiltered results (Record.all).
So a specific scenario is if I'm on the '/records/' route, but I want to filter for Records where name == 'foo'
. As soon as I filter the table, it will show me the results on the table where name == 'foo'
on page 1. Then if I click to go to page 2 to see more filtered results, it will be as if I'm on the unfiltered page 2 and I will lose my results where name == 'foo'
.
So in my RecordsController
I have:
class RecordsController < ApplicationController
def index
@pagy, @records = pagy(Record.all, items: 25)
end
def search
records = Record.where('name LIKE ?', "%#{params[:name]}%")
records = records&.where('type LIKE ?', "%#{params[:type]}%") if params[:type].present?
render(partial: 'records_table', locals: { records: })
end
end
In my index.html.erb
for Records
:
<h1>Records</h1>
<%= render "record_search_form" %>
<%= render "records_table",
records: @records %>
<div class="pag-bar mt-5 pt-5">
<%== pagy_bootstrap_nav(@pagy) if @pagy.pages > 1 %>
</div>
Inside the record_search_form
partial:
<div style="margin:0.75rem">
<%= form_with url: records_search_path, method: :post,
data: {
controller:"records-search",
records_search_target: "form",
turbo_frame:"records_table"} do |form| %>
<%= form.text_field :name %>
<%= form.text_field :type %>
<%= form.button "Search", class:"btn btn-primary" %>
<% end %>
</div>
Inside the records_table
:
<%= turbo_frame_tag "records_table" do %>
<table>
<thead>
<tr>
<th>
ID
</th>
<th>
Name
</th>
<th>
Type
</th>
</tr>
</thead>
<tbody>
<% if records.present? %>
<% records.each do |record| %>
<tr>
<td><%= record.id %></td>
<td><%= record.name %></td>
<td><%= record.type %></td>
</tr>
<% end %>
<% end %>
</tbody>
</table>
<% end %>
I've tried altering my records#search
action to render a different Pagy instance:
def search
records = Record.where('name LIKE ?', "%#{params[:name]}%")
records = records&.where('type LIKE ?', "%#{params[:type]}%") if params[:type].present?
@pagy_results, @results = pagy(records, items: 25)
render(partial: 'records_table', locals: { records: @results })
end
And then including it in the view like this:
<h1>Records</h1>
<%= render "record_search_form" %>
<%= render "records_table",
records: @records %>
<div class="pag-bar mt-5 pt-5">
<%== pagy_bootstrap_nav(@pagy) if @pagy.pages > 1 %>
</div>
**<div class="pag-bar mt-5 pt-5">
<%== pagy_bootstrap_nav(@pagy_search) if @pagy_search.present? && @pagy_search.pages > 1 %>
</div>**
This results in the issue occurring as before (when I click to go to another page of results, it will be as if I have gone to that page number but for unfiltered results).
I've also tried this inside records#search
:
@pagy_search, @response = pagy(records, items: 100, pagy_url_for: 'records/search')
And doing this does not result in any changes either.
# app/controllers/records_controller.rb
class RecordsController < ApplicationController
def index
scope = Record.all
scope = scope.where("name LIKE ?", "%#{params[:name]}%") if params[:name].present?
scope = scope.where("type LIKE ?", "%#{params[:type]}%") if params[:type].present?
@pagy, @records = pagy(scope, items: 25)
end
end
Pagination links should be inside the turbo frame, because you want them to be updated when navigating.
target: "_top"
- because you want other links to navigate outside of a frame and pagination links to be within a frame.
autoscroll: true
- scrolls to the top of search results.
turbo_action: "advance"
- updates current url.
# app/views/records/index.html.erb
<%= render "search_form" %>
<%= turbo_frame_tag "records_table", target: "_top", autoscroll: true,
data: {turbo_action: "advance"} do %>
<%= render "table", records: @records %>
<%== pagy_nav(@pagy, anchor_string: 'data-turbo-frame="_self"') %>
<% end %>
Almost every time, search should be a GET
form:
# app/views/records/_search_form.html.erb
<%= form_with url: records_path, method: :get,
data: {turbo_frame:"records_table"} do |form| %>
<%= form.text_field :name, value: params[:name] %>
<%= form.text_field :type, value: params[:type] %>
<%= form.button "Search", name: nil %>
<% end %>
<!-- app/views/records/_table.html.erb -->
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Type</th>
</tr>
</thead>
<tbody>
<% records.each do |record| %>
<tr>
<td><%= record.id %></td>
<td><%= record.name %></td>
<td><%= record.type %></td>
</tr>
<% end %>
</tbody>
</table>