Membuat Checkbox dengan Multiple Selection pada Rails
Prerequisite
ruby 2.6.3
rails 5.2.3
postgresql 11.5
Prakata
Rails, menyediakan helper method untuk menampilkan multiple checkbox pada view template yang bernama collection_check_boxes
.
Kalau tidak salah, helper method ini mulai diperkenalkan pada Rails versi 4.0.2 hingga sekarang (6.0.0).
Helper ini masuk ke dalam kelas ActionView::Helpers::FormOptionsHelper. Nah, artinya, helper method ini kita gunakan di dalam form.
collection_check_boxes(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block) public
Lebih jauh mengenail helper method ini, dapat teman-teman baca sendiri pada dokumentasi di apidock.com/rails yaa.
Sekenario
Misalkan, saya memiliki 3 pilihan berupa, Adult, Teen, dan Children.
Saya ingin user dapat memilih salah satu, dua atau ketiganya.
Hasil dari inputan checkbox ini akan dimasukkan ke dalam field :age_preference
dengan tipe data string.
Checkbox ini akan harus dapat:
- Memasukkan data, pada saat membuat data baru
:new
- Menampilkan data yang sudah ada pada database, pada saat mengedit data
:edit
- Menampilkan data dengan format (bentuk) yang benar
- Memiliki fitur “Select All” agar mudah dioperasikan
Kira-kira seperti ini hasilnya,
Gambar 1. Multiple checkbox dengan fitur Select All
Gambar 2. Uncheck Select All jika salah satu dari checkbox tidak dicentang
Eksekusi
Saya sudah memiliki field :age_preference
dengan tipe data string pada skema database.
1create_table "experiences", force: :cascade do |t|2 # ...3 # ...4 # ...5 t.string "age_preference"6 # ...7 # ...8 # ...9end
Controller
Bagian controller ini tidak penting pada catatan ini.
Karena pada intinya, saya hanya akan membuat sebuah instance variable yang akan digunakan form di view template.
1class ExperiencesController < ApplicationController2 def index3 # ...4 end56 def show7 # ...8 end910 def new11 @experience = Experience.new12 end1314 def create15 @experience = Experience.new(experience_params)1617 if @experience.save18 redirect_to experience_path(@experience)19 else20 render :new21 end22 end2324 def edit25 @experience = Experience.find(params[:id])26 end2728 def update29 @experience = Experience.find(params[:id])3031 if @experience.update(experience_params)32 redirect_to experience_path(@experience)33 else34 render :edit35 end36 end3738 def destroy39 # ...40 end4142 private4344 def experience_params45 params.require(:experience).permit( ..., :age_preference, ..., ...)46 end47end
Selanjutnya, saya akan membuat tampilan multiple checkbox pada view template.
View
Berdasarkan sekenario di atas, pada nomor 1 dan 2, artinya, saya memiliki form pada saat :new
dan :edit
.
Lebih mudah, dibuatkan render partial agar bisa berbagi form. Wkwkwk.
1<%= form_for(@experience, url: {action: :create}, html: {id: 'form'}) do |f| %>2 <%= render 'form', f: f, experience: @experience %>3<% end %>
1<%= form_for(@experience, url: {action: :update}, html: {id: 'form'}) do |f| %>2 <%= render 'form', f: f, experience: @experience %>3<% end %>
Nah, selanjutnya tinggal membuat view template form yang akan digunakan oleh kedua view template di atas.
Namun, saya akan pangkas hanya pada bagian input untuk :age_preference
saja.
CSS class yang ada pada contoh di bawah, hanya dummy.
1<div class="form-group">2 <label>Age Preference</label>3 <!-- Untuk [] Adult, [] Teen, [] Children -->4 <%= f.collection_check_boxes :age_preference, ['Adult', 'Teen', 'Children'], :to_s, :to_s,5 (experience.age_preference.nil? ? {} : {checked: JSON.parse(experience.age_preference.to_json).split(", ")}) do |b| %>6 <div class="custom-checkbox">7 <%= b.check_box class: 'custom-control-input checkbox-item' %>8 <%= b.label class: 'custom-control-label' %>9 </div>10 <% end %>11 <!-- Untuk [] Select All -->12 <div class="custom-checkbox">13 <%= check_box_tag "Select All", nil, nil, class: "custom-control-input checkbox-all" %>14 <%= label_tag "Select All", nil, class: "custom-control-label" %>15 </div>16</div>
Puanjang sekali yaa? Wkwkwk
Apa kegunaan dari option,
1(experience.age_preference.nil? ? {} : {checked: JSON.parse(experience.age_preference.to_json).split(", ")})
Option tersebut saya tambahan untuk menampilkan data yang sudah ada pada database pada saat proses :edit
. Kalau tanpa baris itu, maka pada saat proses edit, checkbox akan kembali uncheck all.
Nah, kita memerlukan bantuan Javascript, untuk membuat feature “Select All”.
Pada class yang dimiliki oleh check_box
, terdapat 2 kelas yang berbeda.
checkbox-item
untuk Adult, Teen, dan Children. Sdangkan checkbox-all
untuk Select All.
Langsung kita tambahkan Javascript pada bagian paling bawah dari file _form.html.erb
ini.
1<script>2 // For age preference select all feature3 $(".checkbox-checkall").change(function(){4 $(".checkbox-item").prop("checked", $(this).is(":checked"))5 });67 // For unchecked select all checkbox if one of checkbox-item unchecked8 $(".checkbox-item").change(function () {9 if ($(".checkbox-item:checked").length == $(".checkbox-item").length){10 $(".checkbox-checkall").prop("checked",true);11 }12 else {13 $(".checkbox-checkall").prop("checked",false);14 }15 });16</script>
Nah, feature multiple checkbox dengan tambahan select all sudah selesai.
Selanjutnya kita perlu merubah data yang di passing dari view template ini ke model dalam bentuk yang mudah untuk dibaca di database.
Kita akan tambahkan method untuk mempercantik bentuk datanya terlebih dahulu pada model.
Model
Karena data output dari helper method collection_check_boxes
ini berupa array, maka saya akan merubahnya menjadi string karena field :age_preference
bertipe data string. Namun, kalau langsung disimpan, bentuk datanya akan jelek, wkwkwk.
Sebenarnya ini bukan masalah, karena kita dapat membuat helper method lain untuk mengkonversi tampilan data, namun saya lebih memilih untuk merubah bentuk datanya sebelum disimpan. Maka dari itu saya membuat method untuk merubah bentuk data yang akan disimpan pada model.
Bentuk data yang akan disimpan seperti ini apabila belum dirubah.
"[\"Adult\", \"Teen\", \"Children\"]"
Sedangkan, yang saya inginkan seperti ini,
"Adult, Teen, Children"
Nah, ini adalah method yang saya tambahkan pada model, untuk merubah bentuk data hasil dari collection_check_boxes
.
1class Experience < ApplicationRecord2 # ...3 # ...4 # ...56 before_save do7 self.age_preference.gsub!(/[\[\]\"]/, "")&.delete_prefix!(", ") if attribute_present?("age_preference")8 end9end
Dengan begini, pada saat akan menggunakan data :age_preference
untuk digunakan pada view temlate, tidak perlu repot, karena bentuk datanya sudah bagus.
Misal pada :show
.
1Age Preference : <%= @experience.age_preference %>
Mungkin bukan cara yang baik, jadi teman-teman tidak harus mengikutinya atau menggunakan cara yang lebih baik yaa.
Bagaimanapun juga saya masih seorang Junior Rails Developer yang belum memiliki pengalaman yang cukup dalam mendevelop web app yang baik.
Sepertinya segini saja, mudah-mudahan dapat bermanfaat buat teman-teman.
Terima kasih.
(^_^)
Referensi
- apidock.com/rails/v4.0.2/ActionView/Helpers/FormOptionsHelper/collection_check_boxes
Diakses tanggal: 2019/12/10