Sortable Bootstrap Tables (in Rails)

Sau đây mình xin giới thiệu cho các bạn một gem trong rails hỗ trợ các bạn trong việc sắp xếp và thay đổi thứ tự các bản ghi trong danh sách các bản ghi. Nó sẽ hỗ trợ các bạn kéo thả bằng việc sử dụng thư viện của JQuery ( JQuery UI Gem) làm việc sắp xếp trở nên sinh động và dễ dàng hơn. Nào hãy cùng tìm hiểu cách sử dụng nó nhé, nó cũng không quá khó để cài đặt.

I. Tổng quan

Bài viết sẽ giúp các bạn làm sao để thêm sắp xếp bằng cách kéo thả vào bảng trong rails. Ở đây chúng ta sử dụng ranked-modelJQuery-UI

sortable.gif

II. Sortable list gems

ranked.rb

def update_index_from_position
  case position
    when Integer
      neighbors = neighbors_at_position(position)
      min = (neighbors[:lower] ? neighbors[:lower].rank : RankedModel::MIN_RANK_VALUE)
      max = (neighbors[:upper] ? neighbors[:upper].rank : RankedModel::MAX_RANK_VALUE)
      rank_at( ( ( max - min ).to_f / 2 ).ceil + min )
  end
end

ranked-model thực hiện bằng cách tăng thứ hạng nằm giữa khoảng từ -8388607 đến 8388607 (mở rộng của MEDIUMMINT trong MySQL). Điều gì sẽ xảy ra nếu sau vài nghìn lần sắp xếp một danh sách các số được sắp xếp thứ tự sẽ gối lên nhau, hoặc chúng sẽ vượt ra khỏi giới hạn? Trong trường hợp này, tất cả bảng sẽ được cập nhật...

ranked.rb

def assure_unique_position
  if ( new_record? || rank_changed? )
    unless rank
      rank_at( RankedModel::MAX_RANK_VALUE )
    end

    if (rank > RankedModel::MAX_RANK_VALUE) || current_at_rank(rank)
      rearrange_ranks
    end
  end
end

Nói một cách thực tế, nếu số lượng sắp xếp trên web của bạn không quá nhiều thì vấn đề này hoàn toàn có thể được giải quyết. Tuy nhiên, ranked-model khá dễ dàng để cài đặt và không có lý do gì để chúng ta không sử dụng nó.

III. Cài đặt

Cài đặt gem và thêm trường vào model. Gemfile

gem "ranked-model"

models/thing.rb

class Thing < ActiveRecord::Base
  include RankedModel
  ranks :row_order
end

db/migrate/20140908010519_add_row_order_to_things.rb

class AddRowOrderToThings < ActiveRecord::Migration
  def change
    add_column :things, :row_order, :integer
  end
end

Update Row Order Controller Action Đầu tiên chúng ta cần viết hàm để cập nhật việc sắp xếp row_order_position. Nó sẽ cần số thứ tự và id của model cần sắp xếp. Chúng ta sẽ làm rõ ở phần gửi chúng qua AJAX POST ở phần sau.

controllers/admin/things_controller.rb

class ThingsController < ApplicationController

  def index
    @things = Thing.rank(:row_order).all
  end

  def update_row_order
    @thing = Thing.find(thing_params[:thing_id])
    @thing.row_order_position = thing_params[:row_order_position]
    @thing.save

    render nothing: true # this is a POST action, updates sent via AJAX, no view rendered
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_thing
      @thing = Thing.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def thing_params
      params.require(:thing).permit(:thing_id, :title, :description, :row_order_position)
    end
end

Thêm route cho sort config/routes.rb

resources :things do
  post :update_row_order, on: :collection
end

Bây giờ, chúng ta sẽ thêm JavaScript để kéo thả sử dụng JQuery UI Gem Chúng ta cần sortable và highlight modules.

assets/javascripts/admin/application.js

//= require jquery
//= require jquery_ujs
//= require jquery-ui/sortable
//= require jquery-ui/effect-highlight
//= require turbolinks
//= require_tree .

assets/javascripts/admin/update_things_row_order.js.coffee

jQuery ->
  if $('#sortable').length > 0
    table_width = $('#sortable').width()
    cells = $('.table').find('tr')[0].cells.length
    desired_width = table_width / cells + 'px'
    $('.table td').css('width', desired_width)

    $('#sortable').sortable(
      axis: 'y'
      items: '.item'
      cursor: 'move'

      sort: (e, ui) ->
        ui.item.addClass('active-item-shadow')
      stop: (e, ui) ->
        ui.item.removeClass('active-item-shadow')
        # highlight the row on drop to indicate an update
        ui.item.children('td').effect('highlight', {}, 1000)
      update: (e, ui) ->
        item_id = ui.item.data('item-id')
        position = ui.item.index() # this will not work with paginated items, as the index is zero on every page
        $.ajax(
          type: 'POST'
          url: '/things/update_row_order'
          dataType: 'json'
          data: { thing: {thing_id: item_id, row_order_position: position } }
        )
    )

Table Markup Bây giờ, chúng ta sẽ thêm bootstrap table markup. Mỗi hàng của bảng có 1 class của đối tượng chỉ định trong tùy chọn của JQuery UI sortable. Thing.id thích hơpj sẽ được thêm vào mỗi hàng và truyền vào update_row_controller.

views/admin/things/index.html.erb

<table class="table table-bordered table-striped" id="sortable">
  <thead>
    <tr>
      <th>
        Title
      </th>
      <th>
        Description
      </th>
    </tr>
  </thead>
  <tbody>
    <% @things.each do |thing| %>
      <tr data-item-id=<%= thing.id %> class="item">
        <td>
          <%= thing.title %>
        </td>
        <td>
          <%= link_to "show", thing_path(thing), class: "btn btn-default pull-right" %>
        </td>
      </tr>
    <% end %>
  </tbody>
</table>

Vậy là chúng ta đã có thể sắp xếp trong bảng chỉ với một số cài đặt đơn giản. Mong rằng bài viết này có thể giúp các bạn có thêm những lựa chọn cho việc sắp xếp dữ liệu. Các bạn có thể xem thêm tại:

Live demo

Github repo

Chúc các bạn thành công.