Javascript

Backbone: How to implement pagination


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:
http://blog.magmalabs.io/?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:

http://blog.magmalabs.io/?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
Development
Angular 2 Overview
Beginner
ReactJS: Loops in JSX
Javascript
AngularJS Providers under the hood