Sejak memasang "dark" theme, saya cenderung menjadi malas menulis. Untuk sementara, dark theme saya disable dulu yaa. Terima kasih (^_^) (bandithijo, 2024/09/15) ●
Prerequisite
Ruby 2.6.3
Rails 5.2.3
PostgreSQL 11.5
Prakata
Beberapa waktu yang lalu, saya pernah membangun project dengan menggunakan Ransack gem untuk menghandle MetaSearch.
Sedikit penjelasan mengenai Ransack,
Ransack sendiri pengertian singkatnya adalah Object-based searching.
Ditulis ulang dari MetaSearch yang dibuat oleh Ernie Miller dan didevelop/dimaintain selama bertahun-tahun oleh Jon Atack dan Ryan Bigg serta mendapatkan bantuan dari sekelompok contributor yang hebat.
Ransack dapat membantu kita membuat simple dan advanced search forms untuk Rails aplikasi kita.
Jika teman-teman mencari gem untuk menyederhanakan pembuatan query pada model dan controller, Ransack mungkin bukan gem yang tepat. Mungkin bisa mencoba Squeel.
Ransack kompatibel dengan Rails versi 6.0, 5.2, 5.1, 5.0, dan pada Ruby 2.3 ke atas.
Alasan kenapa memilih Ransack, karena Ransack works out-of-the-box pada Active Record.
Instalasi
Seperti biasa, tambahkan pada Gemfile
.
1
2
3
4
# ...
# ...
gem 'ransack', '~> 2.3'
Setelah itu jangan lupa untuk menjalankan,
$ bundle install
Untuk menginstall Ransack pada web aplikasi kita.
Penerapan
Pada catatan kali ini, saya tidak akan menuliskan tentang penggunaan Ransack untuk pencarian dengan menggunakan form search_form_for
.
Namun, saya akan menuliskan penggunaan Ransack untuk membuat tab filter berdasarkan field tertentu. Misal, dalam kasus saya adalah nama negara (country).
Kira-kira seperti ini hasilnya.
Contoh di atas, sudah dapat kita perkirakan bahwa hasil dari object yang sudah difilter akan ditampilkan pada view index.html.erb
.
Controller
Nah, pada bagian controller, isinya sangat orisinil seperti yang dicontohkan pada halaman readme dari Ransack.
1
2
3
4
5
6
7
8
9
10
class CareersController < ApplicationController
def index
@q = Career.ransack(params[:q])
@careers = @q.result(distinct: true).page(params[:page]).per(10)
end
# ...
# ...
# ...
end
Pada model dan route, tidak perlu kita tambahkan apa-apa.
Selanjutnya, tinggal bermain pada view template.
Pada view template, saya mem-passing nilai dari params[:q][:country_cont]
yang dikirimkan oleh view ke controller.
Dapat teman-teman perhatikan, terdapat object country_cont
pada params tersebut, dapet dari mana?
country
adalah salah satu field di dalam tabel careers.
# careers table
+--------------------+-------------------------------------------+-----------+-----------+
| position_name | description | city | country |
|--------------------+-------------------------------------------+-----------+-----------|
| Technology Planner | Responsibilities and Duties: | Singapore | Thailand |
| | - Neutra stumptown literally. | | |
| | - Goth squid yolo etsy cliche kogi beard. | | |
_cont
adalah predicate yang disediakan oleh Ransack yang berarti Contains value.
View
Untuk view template style dari tab, sesuaikan dengan style yang teman-teman gunakan.
Yang saya berikan di bawah, hanya contoh saja.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<nav class="nav">
<% if (params.has_key?(:q)) %>
<%= link_to 'All', careers_path, class: "nav-link" %>
<%= link_to 'Singapore', careers_path(q: {country_cont: 'Singapore'}),
class: "nav-link #{'active' if params[:q][:country_cont] == 'Singapore'}" %>
<%= link_to 'Malaysia', careers_path(q: {country_cont: 'Malaysia'}),
class: "nav-link #{'active' if params[:q][:country_cont] == 'Malaysia'}" %>
<%= link_to 'Thailand', careers_path(q: {country_cont: 'Thailand'}),
class: "nav-link #{'active' if params[:q][:country_cont] == 'Thailand'}" %>
<% else %>
<%= link_to 'All', careers_path, class: "nav-link active" %>
<%= link_to 'Singapore', careers_path(q: {country_cont: 'Singapore'}),
class: "nav-link" %>
<%= link_to 'Malaysia', careers_path(q: {country_cont: 'Malaysia'}),
class: "nav-link" %>
<%= link_to 'Thailand', careers_path(q: {country_cont: 'Thailand'}),
class: "nav-link" %>
<% end %>
</nav>
Hmmm, kurang beautiful yaa…
Wkwkwkwk
Mungkin bisa disederhanakan lagi seperti ini.
1
2
3
4
5
6
7
8
9
<nav class="nav">
<%= link_to 'All', careers_path, class: "nav-link #{params.has_key?(:q) ? '' : 'active'}" %>
<%= link_to 'Singapore', careers_path(q: {country_cont: 'Singapore'}),
class: "nav-link #{'active' if params.has_key?(:q) && params[:q][:country_cont] == 'Singapore'}" %>
<%= link_to 'Malaysia', careers_path(q: {country_cont: 'Malaysia'}),
class: "nav-link #{'active' if params.has_key?(:q) && params[:q][:country_cont] == 'Malaysia'}" %>
<%= link_to 'Thailand', careers_path(q: {country_cont: 'Thailand'}),
class: "nav-link #{'active' if params.has_key?(:q) && params[:q][:country_cont] == 'Thailand'}" %>
</nav>
Dah!
Lumayanlah yaa.
Sebenarnya kita masih dapat membuatnya menjadi lebih dinamis, dengan mengambil data country dari model.
Yuk kita lakukan, agar kode di view template kita lebih compact.
Buat instance variable baru untuk daftar negara-negara pada controller.
1
2
3
4
5
6
7
8
9
10
11
12
class CareersController < ApplicationController
def index
@q = Career.ransack(params[:q])
@careers = @q.result(distinct: true).page(params[:page]).per(10)
@country_list = Career.all.pluck(:country).uniq.sort
end
# ...
# ...
# ...
end
Sekarang kita memiliki instance variable @country_list
yang dapat kita gunakan pada view template.
1
2
3
4
5
6
7
<nav class="nav">
<%= link_to 'All', careers_path, class: "nav-link #{params.has_key?(:q) ? '' : 'active'}" %>
<% @country_list.each do |country| %>
<%= link_to country, careers_path(q: {country_cont: country}),
class: "nav-link #{'active' if params.has_key?(:q) && params[:q][:country_cont] == country}" %>
<% end %>
</nav>
Nah, gimana? Asik kan?
Wkwkwk
Selanjutnya, untuk menampilkan hasil dari index listnya, seperti ini.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
...
<div class="row no-gutters mb-5">
<!-- Available Position -->
<% @careers.each do |career| %>
<div class="col-sm-12">
<div>
<div>
<h6><%= career.position_name.upcase %></h6>
<div>
<i class="icon-location"></i>
<%= career.city.titleize %>, <%= career.country.titleize %>
</div>
</div>
<div>
<%= link_to "View Detail", career_path(career), class: "btn btn-primary" %>
</div>
</div>
</div>
<% end %>
<!-- END Available Position -->
</div>
Selesai!
Apabila berhasil, apabila kita klik tab buttonnya, maka akan menghasilkan list yang sudah terfilter berdasarkan country.
Seperti ilustrasi pada Gambar 1 dan Gambar 2 di atas.
Namun, ada hal yang masih kurang memuaskan.
Saya masih belum dapat membuat URL nya menjadi lebih cantik.
http://localhost:3000/careers?q%5Bcountry_cont%5D=Malaysia
Mungkin akan saya cari pada kesempatan yang lain.
Atau teman-teman punya rekomendasi untuk membuat URL menjadi lebih cantik, boleh tulis pada komentar di bawah yaa.
Update
Nice URL Form
Oke, akhirnya saya berhasil untuk membuat bentuk dari URL menjadi lebih bagus.
Kira-kira akan saya buat seperti ini.
http://localhost:3000/careers?country=Malaysia
Caranya sangat mudah, saya hanya perlu bermain pada router dan controller.
Pertama-tama definiskan url form yang diinginkan pada routes.rb
.
1
2
3
4
# ...
# ...
get 'careers?country=:country', to: 'careers#index', as: 'career_country'
Pendefinisan routing ini, akan menghasilkan sebuah path baru untuk kita, yaitu career_country_path
.
career_country_path GET (/careers?country=:country(.:format) careers#index
Selanjutnya akan saya gunakan pada controller.
Pada instance variable @q
, ubah object params yang ditangkap dari :q
menjadi :country
.
1
2
3
4
5
6
7
8
9
10
11
12
class CareersController < ApplicationController
def index
@q = Career.ransack(country_cont: params[:country])
@careers = @q.result(distinct: true).page(params[:page]).per(10)
@country_list = Career.all.pluck(:country).uniq.sort
end
# ...
# ...
# ...
end
Langkah terakhir, tinggal menggunakan path yang sudah didefinisikan di atas ke view template.
Serta merubah beberapa properti untuk .active
class pada button tab yang aktif.
1
2
3
4
5
6
7
<nav class="nav">
<%= link_to 'All', careers_path, class: "nav-link #{params.has_key?(:country) ? '' : 'active'}" %>
<% @country_list.each do |country| %>
<%= link_to country, career_country_path(country: country),
class: "nav-link #{'active' if params[:country] == country}" %>
<% end %>
</nav>
Selesai.
Sekarang bentuk dari url menjadi lebih bagus.
Saya tidak merekomendasikan menggunakan cara di atas untuk mempercantik URL, karena akan mempersulit apabila ingin dipadukan dengan sorting atau searching.
Oke, sepertinya segini saja.
Mudah-mudahan bermanfaat buat teman-teman.
Terima kasih.
(^_^)
Referensi
-
github.com/activerecord-hackery/ransack
Diakses tanggal: 2019/12/07 -
github.com/activerecord-hackery/meta_search
Diakses tanggal: 2019/12/07 -
ransack-demo.herokuapp.com/
Diakses tanggal: 2019/12/07 -
ransack-demo.herokuapp.com/users/advanced_search
Diakses tanggal: 2019/12/07
Lisensi
Atribusi-NonKomersial-BerbagiSerupa 4.0 Internasional (CC BY-NC-SA 4.0)
Penulis
My journey kicks off from reading textbooks as a former Medical Student to digging bugs as a Software Engineer – a delightful rollercoaster of career twists. Embracing failure with the grace of a Cat avoiding water, I've seamlessly transitioned from Stethoscope to Keyboard. Armed with ability for learning and adapting faster than a Heart Beat, I'm on a mission to turn Code into a Product.
- Rizqi Nur Assyaufi