How to paginate with kaminari and AJAX

German Reynaga
4 min readAug 28, 2019

--

Because of the need of my current team for an explanation about how to use Kaminari gem and integrate using AJAX requests, I am writing this article with the aim of provide some help.

Requirements

  • Ruby 2.5.0^ (This means that you need to have this version as a minimum)
  • Rails 4.2.11^
  • Kaminari gem
  • Jquery gem

In this article I will not stop to explain how to create a Rails Application or to specify point by point how to create models or controllers because there are already many tutorials about it and I don’t want to make this bigger than it is. I will only describe the instructions and hope you can find the corresponding solutions if any problem arises.

So, first thing we are going to do is make sure we have Kaminari and Jquery installed searching or adding the next lines to your Gemfile.

gem 'kaminari'
gem 'jquery-rails'

And then run the next line in your terminal within your app project. This will generate or update your Gemfile.lock

$ bundle install

Then wee need to update our application.js adding the following line at the bottom but before “//= require_tree .“

//= require jquery
//= require jquery_ujs

Now comes the real thing.

First we are going to run the next lines, the first one will create a model Visit and an associated table called visits where records of all visits to somewhere are supposed to be found, the second line creates a controller where we are going to put some functions for paginating records and responsing to AJAX:

$ rails generate model visit name:string gender:string area:string
$ rails generate controller Visits

Model

So we are going to open the model that we can found in app/models/visit.rb and add the misssing line 2 where number_of_pages must be an integer number that represents how many records do you want to have for each page.

class Visit < ApplicationRecord
paginates_per number_of_pages
end

Controller

then we need some method in our controller to obtain the corresponding records from our model.

class VisitsController < ApplicationController
# This method will be required later
def show; end

def visitors
# This parameter will contain the searched page
page = params[:page] || 1
# With this we are able to retrieve current page founded records
visitors = Visit.page(page)
render status: 200, json: { records: visitors }
end
end

But it seems to have something wrong, this is because we need to know how many pages do we have in the model, so let’s add a few lines. It’s important to say that there are many different ways to obtain the total pages count, but I’ll only shw two of them. With this in mind we are going to make some modifications in our controller.

class VisitsController < ApplicationController
# This method will be required later
def show; end
def visitors
# This parameter will contain the searched page
page = params[:page] || 1
# With this we are able to retrieve current page founded records
visitors = Visit.page(page)
render status: 200, json: { records: visitors, total_pages: total_pages }
end
def total_pages
# Remember to change records_per_page with an integer number
@total_pages ||= (Visit.count / records_per_page.to_f).ceil
end
end

The second way is to change our total_pages method with this:

def total_pages
@total_pages ||= Visit.page(1).total_pages
end

Both ways works the same there are executed as the next SQL instruction SELECT COUNT(*) FROM “visits” and it’s your choose which one you will use.

Routes

Okay, at this point we have already setted up our model and basic controller methods, then we need to add an endpoint to make our requests from AJAX, for this we are going to open config/routes.rb and add the missing line 2, also it is necessary to add a route where ajax will work.

Rails.application.routes.draw do
get 'visits/show', to: 'visits#show'
get 'visits/:page', to: 'visits#visitors'
end

Views / AJAX

In this section we will create a new file app/views/visits/show.html.erb where we are going to put our ajax request and show our pagination. For this purpose we create the previous method ‘show’ which will automatically use this new file. First of all add the next content to the specified file.

<script>
var records = undefined;
var totalPages = undefined;
var currentPage = 1;
var currentTotalPages = undefined;
var prevPage = undefined;
var nextPage = undefined;
var firstPage = undefined;
var lastPage = undefined;
$(document).ready(() => {
fetchVisits(1);
})
async function fetchVisits(page) {
currentPage = page * 1
if (page >= 1) {
await $.ajax({
url: `${page}`,
data: {
“page”: page
},
type: “GET”,
success: response => {
records = response.records
if (!totalPages) {
totalPages = totalPages || []
for (let i = currentPage; i <= response.total_pages; i++) {
totalPages.push(i);
}
firstPage = 0;
lastPage = totalPages.length < 5 ? totalPages.length : 5 ;
}
addPageButtons(response)
}
});
}
}
async function addPageButtons(response) {
if (totalPages.includes(currentPage)) {
prevPage = (currentPage — 1) <= 0 ? 1 : currentPage — 1
nextPage = (currentPage) > totalPages.length ? totalPages.length — 1 : currentPage + 1;
if (currentPage > lastPage) {
firstPage = currentPage — 1;
lastPage = firstPage + 5;
} else if (currentPage <= firstPage) {
firstPage = currentPage — 5;
lastPage = firstPage + 5;
}

pagesToShow = totalPages.slice(firstPage, lastPage)
$(‘#pages’).empty();
$(‘#records’).empty();
$(‘#pages’).append(`<button data-page=’${prevPage}’ onClick=’fetchVisits(this.dataset.page)’>Previous</button>`)
for (const i of pagesToShow) {
$(‘#pages’).append(`<button data-page=’${i}’ onClick=’fetchVisits(this.dataset.page)’>${i}</button>`)
}
$(‘#pages’).append(`<button data-page=’${nextPage}’ onClick=’fetchVisits(this.dataset.page)’>Next</button>`)
showRecords()
}
}
function showRecords() {
for(record of records) {
$(‘#records’).append(`<li>${record.created_at}</li>`)
}
}
</script>

With this, we are almost done. The last thing we need is to add this html content above the script in your show.html.erb file.

<div id=”records”>
</div>
<div id=”pages”>
</div>

These two elements are where our responses will be placed.
If this article helped you in any way, I would appreciate it if you shared it.

Github Repository

Here you can found all the code used for this example.

https://github.com/Arquetipo28/kaminari-ajax

--

--

German Reynaga

Full Stack web developer, passionate with education and constantly improving my background.