14. 批次編輯 (Bulk Editing)
14-1 需求說明
在後台編輯資料時,如果資料很多,有沒有什麽 UI 可以一次修改或刪除多筆呢? 這一章將實作透過核選方塊來做批次刪除和修改。
勾選要修改哪些活動,按下最下麵的按鈕就會批次修改。
14-2 批次刪除
先來做批次刪除,首先需要修改 config/routes.rb
,加上一個新的路由來做批次動作:
resources :events do
resources :tickets, :controller => "event_tickets"
+ collection do
+ post :bulk_update
+ end
end
修改 app/views/admin/events/index.html.erb
,用 form_tag
包住整個 table
,並加上核選方塊和批次刪除的按鈕:
+ <%= form_tag bulk_update_admin_events_path, :class => "form-inline" do %>
<table class="table">
<tr>
+ <th><%= check_box_tag "全選", "1", false, :id => "toggle_all" %></th>
# 略
<% @events.each do |event| %>
<tr>
+ <td>
+ <%= check_box_tag "ids[]", event.id %>
+ </td>
# 略
</table>
+ <p><%= submit_tag "批次刪除", :class => "btn btn-danger", :data => { :confirm => "Are you sure?" } %></p>
+ <% end %>
+ <script>
+ // 這個 javascript 會綁事件在 #toggle_all 核選方塊上,來做全選效果
+ $("#toggle_all").click(function(){
+ if ( $(this).prop("checked") ) {
+ $("input[name='ids[]']").prop("checked", true);
+ } else {
+ $("input[name='ids[]']").prop("checked", false);
+ }
+ })
+ </script>
修改 app/controllers/admin/events_controller.rb
,循環 params[:ids]
陣列找出每個 event 然後刪除。
+ def bulk_update
+ total = 0
+ Array(params[:ids]).each do |event_id|
+ event = Event.find(event_id)
+ event.destroy
+ total += 1
+ end
+
+ flash[:alert] = "成功完成 #{total} 筆"
+ redirect_to admin_events_path
+ end
這樣就可以批次刪除了。
Pro Tip 小技巧:關於
Array(params[:ids]
這個用法,如果是Array([1,2,3])
會等同於[1,2,3]
沒變,但是Array[nil]
會變成[]
空陣列,這可以讓.each
方法不會因為nil.each
而爆錯。如果不這樣處理,在沒有勾選任何活動就送出的情況,就會爆出 NoMethodError 錯誤。除非你額外檢查params[:id]
如果是nil
就返回,但不如用Array
來的精巧。
注意本來的 admin layout 中的 flash 樣式有點問題,請修改 app/views/layouts/admin.html.erb
修正一下:
- <div class="container">
+ <div class="container" style="padding-top: 60px">
- <p class="notice"><%= notice %></p>
- <p class="alert"><%= alert %></p>
+ <% if notice %>
+ <p class="notice alert-success"><%= notice %></p>
+ <% end %>
+ <% if alert %>
+ <p class="alert alert-danger"><%= alert %></p>
+ <% end %>
14-3 批次修改
接下來實作批次修改狀態。注意到我們已經包了一個 form
在 table
外面了:
- 一個 form 的送出 URL 位置是固定的,會進到同一個 action。這裡會 POST 送出到
bulk_update_admin_events_path
form
裡面不能再包form
那我們要怎麽區別到底是要刪除還是修改?所幸,按鈕的文字也會送出變成參數。請修改 app/views/admin/events/index.html.erb
,加上批次修改的按鈕,以及狀態的下拉選單:
</table>
- <p><%= submit_tag "批次刪除", :class => "btn btn-danger", :data => { :confirm => "Are you sure?" } %></p>
+ <p>
+ <%= select_tag :event_status, options_for_select( Event::STATUS.map{ |s| [t(s, :scope => + "event.status"), s] }), :class => "form-control" %>
+
+ <%= submit_tag t(:bulk_update), :class => "btn btn-primary" %>
+ <%= submit_tag t(:bulk_delete), :class => "btn btn-danger", :data => { :confirm => "Are you + sure?" } %>
+ </p>
<% end %>
修改 config/locales/zh-CN.yml
"zh-CN":
+ bulk_update: 批次編輯
+ bulk_delete: 批次刪除
如果要做英文版,請再編輯 en.yml 加上英文翻譯即可
如果觀察一下 HTML 源碼,可以看到按鈕也是有參數的:
接著修改 app/controllers/admin/events_controller.rb
,根據 params[:commit]
參數,就可以判斷用戶當初是按下刪除還是修改了:
Array(params[:ids]).each do |event_id|
event = Event.find(event_id)
- event.destroy
- total += 1
+
+ if params[:commit] == I18n.t(:bulk_update)
+ event.status = params[:event_status]
+ if event.save
+ total += 1
+ end
+ elsif params[:commit] == I18n.t(:bulk_delete)
+ event.destroy
+ total += 1
+ end
end
Pro Tip 小技巧:這裡我改用了 I18n 來顯示按鈕字串,這倒不是因為一定要支援多語言,而是因為文案可能會改。如果你按鈕寫死
<%= submit_tag '批次修改'
的話,那麽在 controller 中也需要寫成if params[:commit] == '批次修改'
。將來那一天要改字,就要記得兩個地方都要改到。但是如果用 I18n 來處理,就之後只要記得改翻譯檔一個地方就好了。這個原則就叫做 DRY: Don’t repeat yourself,這是一個寫好程序的基本原則。