BanditHijo.dev

Memasang Pagination dengan Pagy pada Rails

Created at: 2020-02-20
Author by: BanditHijo

Prerequisite

ruby 2.6.3 rails 5.2.3 postgresql 11.5

Prakata

Bos ditempat saya bekerja, pernah membagikan sebuah tulisan mengenai perbandingan dari 3 gems yang digunakan untuk membantu developer dalam membuat fitur pagination pada web aplikasi yang mereka bangun. 2 diantara gems tersebut adalah gems yang sudah terkenal (setidaknya yang saya tahu seperti itu) dan sudah sering menjadi pilihan.

3 gems tersebut adalah:

  1. Kaminari
  2. Will Paginate
  3. Pagy

Kenapa Pagy?

Coba teman-teman melihat sebentar pada artikel mengenai perbandingan diantara 3 pagination gems tersebut.

Cek di sini.

Pada artikel tersebut, Pagy benar-benar tidak terkalahkan.

Wajar saja, karena dari konsep dasar yang digunakan oleh Pagy itu sendiri,

  1. Pagination is simple task
  2. Keep it simple
  3. Stay away from busines logic
  4. No Rails engine needed
  5. dst.

Dari artikel tersebut, hasil yang diberika Pagy sangat mengintimidasi pembaca yang menggunakan 2 gems yang lain untuk menangani pagination. Terutama saya, sebagai Rails developer yang masih baru.

Setelah membaca-baca dan mencoba-coba sedikit, saya pun memutuskan untuk memigrasikan pagination pada proyek yang sedang saya kerjakan yang sebelumnya menggunakan Kaminari.

Migrasi Kaminari to Pagy

Langkah-langkah migrasi yang saya lakukan adalah, saya tidak langsung menghapus Kaminari gem dari Gemfile. Namun, menambahkan Pagy gem.

Gemfile
1# ...
2# ...
3gem 'kaminari', '~> 1.1', '>= 1.1.1'
4gem 'pagy', '~> 3.7', '>= 3.7.2'
5# ...
6# ...

Jalankan bundle install

$ bundle install

Setelah selesai, tambahkan module Pagy::Backend dan Pagy::Frontend.

app/controllers/application_controller.rb
1class ApplicationController < ActionController::Base
2 include Pagy::Backend
3
4 ...
5 ...
6end
app/helpers/application_helper.rb
1module ApplicationHelper
2 include Pagy::Frontend
3 ...
4 ...
5end

Kemudian, tambahkan pagy initializer apabila diperlukan.

config/initializers/pagy.rb
1# encoding: utf-8
2# frozen_string_literal: true
3
4# Pagy initializer file (3.7.2)
5# Customize only what you really need and notice that Pagy works also without any of the following lines.
6# Should you just cherry pick part of this file, please maintain the require-order of the extras
7
8
9# Extras
10# See https://ddnexus.github.io/pagy/extras
11
12
13# Backend Extras
14
15# Array extra: Paginate arrays efficiently, avoiding expensive array-wrapping and without overriding
16# See https://ddnexus.github.io/pagy/extras/array
17# require 'pagy/extras/array'
18
19# Countless extra: Paginate without any count, saving one query per rendering
20# See https://ddnexus.github.io/pagy/extras/countless
21# require 'pagy/extras/countless'
22# Pagy::VARS[:cycle] = false # default
23
24# Elasticsearch Rails extra: Paginate `ElasticsearchRails::Results` objects
25# See https://ddnexus.github.io/pagy/extras/elasticsearch_rails
26# require 'pagy/extras/elasticsearch_rails'
27
28# Searchkick extra: Paginate `Searchkick::Results` objects
29# See https://ddnexus.github.io/pagy/extras/searchkick
30# require 'pagy/extras/searchkick'
31
32
33# Frontend Extras
34
35# Bootstrap extra: Add nav, nav_js and combo_nav_js helpers and templates for Bootstrap pagination
36# See https://ddnexus.github.io/pagy/extras/bootstrap
37# require 'pagy/extras/bootstrap'
38
39# Bulma extra: Add nav, nav_js and combo_nav_js helpers and templates for Bulma pagination
40# See https://ddnexus.github.io/pagy/extras/bulma
41# require 'pagy/extras/bulma'
42
43# Foundation extra: Add nav, nav_js and combo_nav_js helpers and templates for Foundation pagination
44# See https://ddnexus.github.io/pagy/extras/foundation
45# require 'pagy/extras/foundation'
46
47# Materialize extra: Add nav, nav_js and combo_nav_js helpers for Materialize pagination
48# See https://ddnexus.github.io/pagy/extras/materialize
49# require 'pagy/extras/materialize'
50
51# Navs extra: Add nav_js and combo_nav_js javascript helpers
52# Notice: the other frontend extras add their own framework-styled versions,
53# so require this extra only if you need the unstyled version
54# See https://ddnexus.github.io/pagy/extras/navs
55# require 'pagy/extras/navs'
56
57# Semantic extra: Add nav, nav_js and combo_nav_js helpers for Semantic UI pagination
58# See https://ddnexus.github.io/pagy/extras/semantic
59# require 'pagy/extras/semantic'
60
61# UIkit extra: Add nav helper and templates for UIkit pagination
62# See https://ddnexus.github.io/pagy/extras/uikit
63# require 'pagy/extras/uikit'
64
65# Multi size var used by the *_nav_js helpers
66# See https://ddnexus.github.io/pagy/extras/navs#steps
67# Pagy::VARS[:steps] = { 0 => [2,3,3,2], 540 => [3,5,5,3], 720 => [5,7,7,5] } # example
68
69
70# Feature Extras
71
72# Headers extra: http response headers (and other helpers) useful for API pagination
73# See http://ddnexus.github.io/pagy/extras/headers
74# require 'pagy/extras/headers'
75# Pagy::VARS[:headers] = { page: 'Current-Page', items: 'Page-Items', count: 'Total-Count', pages: 'Total-Pages' } # default
76
77# Support extra: Extra support for features like: incremental, infinite, auto-scroll pagination
78# See https://ddnexus.github.io/pagy/extras/support
79# require 'pagy/extras/support'
80
81# Items extra: Allow the client to request a custom number of items per page with an optional selector UI
82# See https://ddnexus.github.io/pagy/extras/items
83# require 'pagy/extras/items'
84# Pagy::VARS[:items_param] = :items # default
85# Pagy::VARS[:max_items] = 100 # default
86
87# Overflow extra: Allow for easy handling of overflowing pages
88# See https://ddnexus.github.io/pagy/extras/overflow
89# require 'pagy/extras/overflow'
90# Pagy::VARS[:overflow] = :empty_page # default (other options: :last_page and :exception)
91
92# Metadata extra: Provides the pagination metadata to Javascript frameworks like Vue.js, react.js, etc.
93# See https://ddnexus.github.io/pagy/extras/metadata
94# you must require the shared internal extra (BEFORE the metadata extra) ONLY if you need also the :sequels
95# require 'pagy/extras/shared'
96# require 'pagy/extras/metadata'
97# For performance reason, you should explicitly set ONLY the metadata you use in the frontend
98# Pagy::VARS[:metadata] = [:scaffold_url, :count, :page, :prev, :next, :last] # example
99
100# Trim extra: Remove the page=1 param from links
101# See https://ddnexus.github.io/pagy/extras/trim
102# require 'pagy/extras/trim'
103
104
105
106# Pagy Variables
107# See https://ddnexus.github.io/pagy/api/pagy#variables
108# All the Pagy::VARS are set for all the Pagy instances but can be overridden
109# per instance by just passing them to Pagy.new or the #pagy controller method
110
111
112# Instance variables
113# See https://ddnexus.github.io/pagy/api/pagy#instance-variables
114# Pagy::VARS[:items] = 20 # default
115
116
117# Other Variables
118# See https://ddnexus.github.io/pagy/api/pagy#other-variables
119# Pagy::VARS[:size] = [1,4,4,1] # default
120# Pagy::VARS[:page_param] = :page # default
121# Pagy::VARS[:params] = {} # default
122# Pagy::VARS[:anchor] = '#anchor' # example
123# Pagy::VARS[:link_extra] = 'data-remote="true"' # example
124
125
126# Rails
127
128# Rails: extras assets path required by the helpers that use javascript
129# (pagy*_nav_js, pagy*_combo_nav_js, and pagy_items_selector_js)
130# See https://ddnexus.github.io/pagy/extras#javascript
131# Rails.application.config.assets.paths << Pagy.root.join('javascripts')
132
133
134# I18n
135
136# Pagy internal I18n: ~18x faster using ~10x less memory than the i18n gem
137# See https://ddnexus.github.io/pagy/api/frontend#i18n
138# Notice: No need to configure anything in this section if your app uses only "en"
139# or if you use the i18n extra below
140#
141# Examples:
142# load the "de" built-in locale:
143# Pagy::I18n.load(locale: 'de')
144#
145# load the "de" locale defined in the custom file at :filepath:
146# Pagy::I18n.load(locale: 'de', filepath: 'path/to/pagy-de.yml')
147#
148# load the "de", "en" and "es" built-in locales:
149# (the first passed :locale will be used also as the default_locale)
150# Pagy::I18n.load({locale: 'de'},
151# {locale: 'en'},
152# {locale: 'es'})
153#
154# load the "en" built-in locale, a custom "es" locale,
155# and a totally custom locale complete with a custom :pluralize proc:
156# (the first passed :locale will be used also as the default_locale)
157# Pagy::I18n.load({locale: 'en'},
158# {locale: 'es', filepath: 'path/to/pagy-es.yml'},
159# {locale: 'xyz', # not built-in
160# filepath: 'path/to/pagy-xyz.yml',
161# pluralize: lambda{|count| ... } )
162
163
164# I18n extra: uses the standard i18n gem which is ~18x slower using ~10x more memory
165# than the default pagy internal i18n (see above)
166# See https://ddnexus.github.io/pagy/extras/i18n
167# require 'pagy/extras/i18n'
168
169# Default i18n key
170# Pagy::VARS[:i18n_key] = 'pagy.item_name' # default

Secara default, semua konfigurasinya dalam keadaan terdisable.

Teman-teman dapat memilih mana-mana saja yang teman-teman perlukan.

Misalkan seperti default items per satu halaman, secara default berjumlah 20.

Atau contoh lain, untuk frontend.

config/initializers/pagy.rb
1# ...
2# ...
3
4# Frontend Extras
5
6# Bootstrap extra: Add nav, nav_js and combo_nav_js helpers and templates for Bootstrap pagination
7# See https://ddnexus.github.io/pagy/extras/bootstrap
8require 'pagy/extras/bootstrap'
9
10# Bulma extra: Add nav, nav_js and combo_nav_js helpers and templates for Bulma pagination
11# See https://ddnexus.github.io/pagy/extras/bulma
12# require 'pagy/extras/bulma'
13
14# Foundation extra: Add nav, nav_js and combo_nav_js helpers and templates for Foundation pagination
15# See https://ddnexus.github.io/pagy/extras/foundation
16# require 'pagy/extras/foundation'
17
18# Materialize extra: Add nav, nav_js and combo_nav_js helpers for Materialize pagination
19# See https://ddnexus.github.io/pagy/extras/materialize
20# require 'pagy/extras/materialize'
21
22# ...
23# ...

Saya menggunakan bootstrap, maka saya memilih frontend extra untuk Bootstrap theme, agar tampilan pagination pada view template saya mengunakan Bootstrap theme.

Controller

Selanjutnya pada controller, bisa memigrasikan object query yang menggunakan Kaminary menjadi Pagy, seperti contoh di bawah.

Kaminary

app/controllers/admin/users_controller.rb
1class Admin::UsersController < AdminsController
2 def index
3 @users = User.order(id: :desc).page(params[:page])
4 end
5
6 # ...
7end

Pagy

app/controllers/admin/users_controller.rb
1class Admin::UsersController < AdminsController
2 def index
3 @pagy, @users = pagy(User.order(id: :desc), items: 25)
4 end
5
6 # ...
7end

View

Kemudian pada view template, saya akan menambahkan direktori app/views/pagy/ dan menambahkan Bootstrap theme _bootstrap_nav.html.erb.

Tujuannya agar mudah untuk melakukan kostumisasi.

app/views/pagy/_bootstrap_nav.html.erb
1<%#
2 This template is i18n-ready: if you don't use i18n, then you can replace the pagy_t
3 calls with the actual strings ("&lsaquo; Prev", "Next &rsaquo;", "&hellip;").
4
5 The link variable is set to a proc that returns the link tag.
6 Usage: link.call( page_number [, text [, extra_attributes_string ]])
7-%>
8<% link = pagy_link_proc(pagy, 'class="page-link"') -%>
9<%# -%><nav aria-label="pager" class="pagy-bootstrap-nav pagination" role="navigation">
10<%# -%> <ul class="pagination">
11<% if pagy.prev -%> <li class="page-item prev"><%== link.call(pagy.prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"') %></li>
12<% else -%> <li class="page-item prev disabled"><a href="#" class="page-link"><%== pagy_t('pagy.nav.prev') %></a></li>
13<% end -%>
14<% pagy.series.each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36] -%>
15<% if item.is_a?(Integer) -%> <li class="page-item"><%== link.call(item) %></li>
16<% elsif item.is_a?(String) -%> <li class="page-item active"><%== link.call(item) %></li>
17<% elsif item == :gap -%> <li class="page-item disabled gap"><a href="#" class="page-link"><%== pagy_t('pagy.nav.gap') %></a></li>
18<% end -%>
19<% end -%>
20<% if pagy.next -%> <li class="page-item next"><%== link.call(pagy.next, pagy_t('pagy.nav.next'), 'aria-label="next"') %></li>
21<% else -%> <li class="page-item next disabled"><a href="#" class="page-link"><%== pagy_t('pagy.nav.next') %></a></li>
22<% end -%>
23<%# -%> </ul>
24<%# -%></nav>

Nah, mantap, kalo sudah tinggal merubah helper method yang dimiliki kaminari pada view template.

Pertama-tama, .total_count. Ini adalah helper method yang disediakan oleh kaminari untuk mentotal jumlah dari collection.

Kamniari

app/views/admin/users/index.html.erb
1...
2...
3 <%= @users.total_count %>
4...
5...

Pagy

app/views/admin/users/index.html.erb
1...
2...
3 <%= @pagy.count %>
4...
5...

Kedua, pada bagian paginationnya akan seperti ini.

Kaminari

app/views/admin/users/index.html.erb
1...
2...
3 <!-- Kaminari Pagination -->
4 <div class="d-flex justify-content-center">
5 <%= paginate @users %>
6 </div>
7 <!-- END Kaminari Pagination -->
8...
9...

Pagy

app/views/admin/users/index.html.erb
1...
2...
3 <!-- Pagy Pagination -->
4 <div class="d-flex justify-content-center">
5 <%== render partial: 'pagy/bootstrap_nav', locals: {pagy: @pagy} %>
6 </div>
7 <!-- END Pagy Pagination -->
8...
9...

Atau seperti ini apabila jumlah halaman hanya satu.

app/views/admin/users/index.html.erb
1...
2...
3 <!-- Pagy Pagination -->
4 <div class="d-flex justify-content-center">
5 <%== render partial: 'pagy/bootstrap_nav', locals: {pagy: @pagy} if @pagy.pages > 1 %></div>
6 <!-- END Pagy Pagination -->
7...
8...

Selanjutnya saya hanya perlu merubah semua collection pada controller dan view helper dari Kaminari ke Pagy.

Setelah dipastikan semuanya sudah dirubah, bisa dicoba untuk menghapus Kamniari dari Gemfile kemudian juga menghapus elemen-elemen lain yang digunakan oleh Kaminari dalam struktur direktori web aplikasi kita, misalnya Kaminari view template termasuk Kamniari initializer config.

Sekian.

Sepertinya segini saja yang dapat saya catat dan bagikan.

Mudah-mudahan dapat bermanfaat bagi teman-teman yang memerlukan.

Terima kasih.

(^_^)

Referensi

  1. ddnexus.github.io/pagination-comparison/gems.html
    Diakses tanggal: 2020/02/20

  2. ddnexus.github.io/pagy/migration-guide
    Diakses tanggal: 2020/02/20

  3. ddnexus.github.io/pagy/
    Diakses tanggal: 2020/02/20

  4. github.com/ddnexus/pagy
    Diakses tanggal: 2020/02/20