Xóa Nhiều Record Đã Lựa Chọn Bởi Checkbox Trong Rails 4

Trong qúa trình phát triển ứng dụng web với Rails, mình gặp một vấn đề cần giải quyết đó là xóa đi một số records mà người dùng lựa chọn trong danh sách các records thông qua checkbox.

Có rất nhiều cách để làm việc này, dưới đây mình trình bày 2 cách đó là

  • xóa thông thường không sử dụng ajax
  • xóa sử dụng ajax

Trước tiên để tiện theo dõi mình lấy một ví dụ là:

Mình có một bảng danh sách các products được liệt kê theo dạng bảng và mỗi dòng có 1 nút chọn (checkbox) cho product đó.

Một button để submit xóa các products đã chọn.

Xóa không sử dụng ajax

Hiển thị dạng bảng như sau:

views/products/index.slim

= form_tag "", method: :delete, data: {confirm: "Are you sure?"}
  table.table.table-striped.table-hover.table-bordered
    thead
      tr
        th.text-center= t(".action")
        th= t(".product_name")
        th= t(".product_kind")
        th= t(".jan_code")
    tbody
      - @products.each do |product|
        tr
          td.text-center= check_box_tag :product_ids, product.id, false,
            class: "check-box-field"
          td= product.name
          td= product.product_kind
          td= product.jan_code
  = submit_tag "Xóa"

Tới đây mình sẽ có lựa chọn cho việc xóa các record đã checked.

  • viết một hàm mới và tạo collection cho nó với phương thức delete
  • tạo một controller mới và cho nó là singular resource

Với cách thứ nhất thì sẽ không theo RESTful có thể làm như sau

Trong products_controller.rb

    def destroy_selected
      if params[:product_ids]
        Product.where(id: params[:product_ids]).destroy_all
      end
      redirect_to products_path
    end

Trong routes.rb

resources :products do
  collection do
    delete :destroy_selected
  end
end

Cấu hình routes như vậy cho mình đường dẫn destroy_selected_products_path trỏ tới hàm destroy_selected trên.

Sửa lại dòng sau trong products/index.slim

= form_tag "", method: :delete, data: {confirm: "Are you sure?"}

chuyển thành

= form_tag destroy_selected_products_path(product_ids: params[:product_ids]), method: :delete, data: {confirm: "Are you sure?"}

Như vậy khi submit thì sẽ xóa các lựa chọn records

Tuy nhiên để tuân thủ RESTful mình làm như sau

Tạo một controller mới tên là bulk_products

rails g controller bulk_products destroy

Hàm destroy trong controller này làm tương tự như destroy_selected ở phía trên.

Trong routes.rb cấu hình

  scope :products do
    resource :bulk_product
  end

Cấu hình như vậy cho mình đường dẫn bulk_product_path với phương thức delete trỏ tới hàm destroy

Như vậy sửa lại dòng sau trong products/index.slim

= form_tag "", method: :delete, data: {confirm: "Are you sure?"}

Thành

= form_tag bulk_product_path(product_ids: params[:product_ids]), method: :delete, data: {confirm: "Are you sure?"}

Sử dụng ajax

Một số trường hợp mình không sử dụng cách thông thường như trên với form_tag, do vậy mình sử dụng ajax để làm việc này.

Về cơ bản thì vẫn là post một mảng các id của products đã chọn để xóa lên server.

Mình vẫn sử dụng controllerbulk_products ở phía trên. Hàm destroy sửa lại một chút như sau:

    def destroy
      if params[:product_ids].present?
        Product.where(id: params[:product_ids]).destroy_all
      end
      respond_to do |format|
        format.json{head :no_content}
      end
    end

Sửa lại views/products/index.slim như sau

#destroy-selected-products.fa.fa-trash-o
#products
  table.table.table-striped.table-hover.table-bordered
    thead
      tr
        th.text-center= t(".action")
        th= t(".product_name")
        th= t(".product_kind")
        th= t(".jan_code")
    tbody
      - @products.each do |product|
        tr.product
          td.text-center= check_box_tag :product_ids, product.id, false,
            class: "check-box-field"
          td= product.name
          td= product.product_kind
          td= product.jan_code

Như vậy khi mình lựa chọn các products rồi bấm vào biểu tượng trash (thùng rác) thì sẽ xóa các records đó.

Thêm code vào products.coffee như sau:

$ ->
  $("#destroy-selected-products").on "click", ->
    product_ids = getSelectedCheckbox($("#products"))
    confirm_msg = "Are you sure?"

    if (product_ids.length > 0) && confirm(confirm_msg)
      $.ajax
        type: "DELETE"
        dataType: "JSON"
        url: "/products/bulk_product"
        data: {product_ids: product_ids}
        success: ->
          $("#products").find(".check-box-field:checked").
            parents(".product").remove()

  return

getSelectedCheckbox = ($elem) ->
  $elem.find(".check-box-field:checked").map((_, elem) ->
    $(elem).val()
  ).get()

Như vậy mình đã thực hiện việc xóa nhiều records lựa chọn bằng 2 cách. 😉