Step by step Pagination using Kaminari, Rails, Coffeescript, Backbone and Underscore
In this tutorial I'm going to show you how to implement pagination using Kaminari, Ruby on Rails, Coffeescript, Backbone js, Underscore js and Eco templates. I created a screencast to help you in the process. But you can also follow the post and do it on your own.
For those of you who like screencasts:
Important links(resources):
How to implement uploads in backbone:
https://34.136.44.183/?p=161
Here's the base repository to start this tutorial:
https://github.com/heridev/jqueryfileuploadbackbone
The final code (using pagination):
https://github.com/heridev/jqueryfileuploadbackbone/tree/backbone_pagination_example
You can see the working demo in the following url:
http://salty-wildwood-1098.herokuapp.com/
So, let’s start by cloning the repo. To do this we need to run code from console:
$ git clone git@github.com:heridev/jqueryfileuploadbackbone.git
Now let’s go into the project directory
$ cd jqueryfileuploadbackbone
and
$ bundle install
The next step: adding the Kaminari Gem
Now we are going to open our Gemfile
and add this
gem 'kaminari
And now, we need to run bundle install again.
$ bundle install
The first file that we are going to edit is the router file called "qr codes router"
app/assets/javascripts/routers/qrcodes_router.js.coffee
The next code that we need to add is used to define the two main routes in our backbone:
class Jqueryfileuploadbackbone.Routers.Qrcodes extends Backbone.Router
routes:
'' : 'showManageImages'
'qrcodes/:page' : 'showManageImages'
'qrcode/add' : 'addNewImage'
And now we need to declare our function to show the manage images view.
The most important thing to explain here is the page number. By default we are using "page 1", if the user does not specify another page number (page = 1)
showManageImages: (page = 1) ->
$('#container-app').html JST['helpers/loading']
#now it’s time to declare the model
# After the line we are declaring our model and
model = new Jqueryfileuploadbackbone.Models.Qrcode()
# and the view
# to show all the images we need to specify in “el” element the div
# which is used to attach that view and when we do the fetch data the view
# is prepared to show all elements in the DOM and the data.
indexView = new Jqueryfileuploadbackbone.Views.QrcodesIndexView
model: model
el: $('#container-app')
indexView.model.fetch data: { page: page }
Now add the loading template:
First we need to create the directory helpers, and create the loading file
app/assets/templates/helpers/loading.jst.eco.haml
And the content is just a div with a loading image
#cargando{style: 'width: 150px; margin-left: auto; margin-right: auto' }
= image_tag 'loading.gif'
If we look at the content of the template, we can see that we are using an image, and for that reason you need to add an image to app/assets/images path If you want to see a loading image before rendering the images list.
Next step: create QrcodesIndex view and its template
Let's create the file in
app/assets/javascripts/views/qrcodes/qrcodes_index.js.coffee
And this is the content:
class Jqueryfileuploadbackbone.Views.QrcodesIndexView extend Backbone.View
# in the initialize method we add the next code
# because we need to specify that we want to render the template when the model changes its data
initialize: ->
@model.on "change", @render, @
helpers: ->
blockPagination: ->
segment = @model.get('perPage')
current = @model.get('current_page') || 1
range = _.range (current - segment), (current + segment+1), 1
middle = _.filter range, (num) =>
num > 0 and num <= @model.get('total_pages')
nextPage: ->
value = @model.get('current_page')
total_pages = @model.get('total_pages')
value = value + 1 unless value is total_pages
value
previousPage: ->
value = @model.get('current_page')
value = value - 1 unless value is 1
value
currentPage: ->
@model.get('current_page')
totalPages: ->
@model.get('total_pages')
template: JST['qrcodes/index']
render: ->
# we need to merge the main view with the helpers function if we want to be able to use it in the template with those functions.
@$el.html @template _.extend(@, @helpers)
@addOnebyOne @model.get('models')
@
# In the addOnebyOne function we use a function from Underscore js called _.each
# to iterate through the models and append each model in a
# new view called QrcodesShow.
addOnebyOne: (models) ->
_.each models, ((qrcode) ->
view = new Jqueryfileuploadbackbone.Views.QrcodesShowView model: qrcode
@$('#qrcodes').append view.render().el
), @
Additionally, I want to talk about the helper’s section:
If you need any additional function, just add it in the helpers sections.
First, to create the Qrcodesshow view, we are going to create the template for QrcodesIndexView:
app/assets/templates/qrcodes/index.jst.eco.haml
with this content:
<%- JST['helpers/pagination'](@) %>
%br
%br
#qrcodes
= link_to 'Add new Image', '#qrcode/add'
%br
%br
<%- JST['helpers/pagination'](@) %>
If we look at the content, we are using an additional template called "pagination" with this content:
app/assets/templates/helpers/pagination.jst.eco.haml
In this template, we are just using our functions declared in helpers section in QrcodesIndexView
%a{ href: '#qrcodes/<%= @previousPage() %>', id: 'page-prev' ,class: "no-underline <%= 'disabled' if @currentPage() is 1 %>" } Prev
<% for page in @blockPagination(): %>
%a{ href: '#qrcodes/<%= page %>', id: 'page-<%= page %>', class: "<%= 'active' if page is @currentPage() %>", data: { page: '<%= page %>' } } <%= page %>
<% end %>
%a{ href: '#qrcodes/<%= @nextPage() %>', id: 'page-next', class: "no-underline <%= 'disabled' if @currentPage() is @totalPages() %>", data: { page: '<%= @currentPage() %>' } } Next
To hide the "previous" or "next" button when there are not many images, we can use this simple CSS code, in my case located in:
app/assets/stylesheets/qrcodes.css.scss
with the content:
.disabled{display: none;}
So, let’s continue with the QrcodesShow located in :
app/assets/javascripts/views/qrcodes/show.js.coffee
The content
class Jqueryfileuploadbackbone.Views.QrcodesShowView extends Backbone.View
# we specify the template for the view
template: JST['qrcodes/show']
# and the render method to show the image
render: ->
@$el.html @template qrcode: @model
@
and the file:
app/assets/templates/qrcodes/show.jst.eco.haml
the content of the template:
%a{href: '#', alt: "<%= @image_file_name %>"}
%img{ alt: "<%= @qrcode.id %>", width: "200", src: "/system/qrcodes/<%= @qrcode.image_file_name %>"}
The remaining part of this tutorial we are going to do all the necessary backend's code.
Now we need to create an additional controller in my case it is:
app/controllers/main_controller.rb
Content:
class MainController < ApplicationController
def index ; end
end
And rename:
app/views/qrcodes
into
app/views/main
Now let’s open the file:
app/controllers/qrcodes_controller.rb
and we need to add this content to use Kaminari:
class QrcodesController < ApplicationController
PER_PAGE_RECORDS = 3
def index
qrcodes_paginated = Qrcode.order('id').page(params[:page]).per(PER_PAGE_RECORDS)
respond_to do |format|
format.json { render :json => {:models => qrcodes_paginated, :current_page => params[:page].to_i, :perPage => PER_PAGE_RECORDS, :total_pages => qrcodes_paginated.num_pages } }
end
end
end
And we need to configure our routes:
config/routes.rb
the content:
get "qrcodes/create"
scope "api" do
resources :qrcodes, format: false
end
root :to => 'main#index'
Now if we run our web server from console:
$ rails s
We can see this... and yaaay! it works!
Important links(resources):
How to implement uploads in backbone:
https://34.136.44.183/?p=161
The base repository to start this tutorial:
https://github.com/heridev/jqueryfileuploadbackbone
The final code(using pagination):
https://github.com/heridev/jqueryfileuploadbackbone/tree/backbone_pagination_example
You can see working the demo in the following url:
http://salty-wildwood-1098.herokuapp.com/
I hope you enjoyed this post! Let me know if the video helped you. See you later!
Heriberto Perez Magaña
Software Engineer at MagmaLabs.io
www.heridev.com.mx