[Java] [Rails] Implementation of drag & drop function (with effects)

2 minute read

Target

ezgif.com-video-to-gif.gif

Development environment

・Ruby: 2.5.7 Rails: 5.2.4 ・Vagrant: 2.2.7 ・VirtualBox: 6.1 ・OS: macOS Catalina

Premise

Implemented below.

Slim introductionBootstrap3 installedPosting function implementation

Implementation

1. Introduce Gem

Gemfile


# Addition
gem'jquery-ui-rails'
gem'ranked-model'

jquery-ui-rails ➡︎ Enable to use jQuery UI.

gem'ranked-model' ➡︎ Be able to manage the order of books.

3. Add column

terminal


$ rails g migration AddRowOrderToBooks row_order:integer

terminal


$ bundle

schema.rb


create_table "books", force: :cascade do |t|
  t.integer "user_id"
  t.string "title"
  t.text "body"
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
  t.integer "row_order"
end

4. Edit the model

Enable ranked-model in Book model.

book.rb


# Addition
include RankedModel
ranks :row_order

5. Edit controller

** ① edit the index action **

books_controller.rb


# Change before
@books = Book.all

# After change
@books = Book.rank(:row_order)

** ② Add row_order_position to the strong parameters. **

The column name is row_order, but here it is described as row_order_position due to the use of Gem.

books_controller.rb


def book_params
  params.require(:book).permit(:title, :body, :row_order_position)
end

** ③ Added sort action **

books_controller.rb


def sort
  book = Book.find(params[:book_id])
  book.update(book_params)
  render body: nil
end

render body: nil ➡︎ Perform only the action, not the view.

6. Edit routing

route.rb


# Change before
resources :books

# After change
resources :books do
  put :sort
end

7. Edit the view

slim:books/index.html.slim


/ Add a class called "table-sortable"
table.table.table-bordered.table-sortable
  thead
    tr
      th
        | Contributor
      th
        | Title
      th
        | Body
      th

  tbody
    [email protected] do |book|
      Add class to /tr tag and set data
      tr.item(data = (model_name: book.class.name.underscore, update_url: book_sort_path(book) })
        td
          = link_to book.user do
            = book.user.name
        td
          = link_to book.title, book_path(book)
        td
          = book.body
        td
          -if book.user == current_user
            = link_to'Delete', book, method: :delete, data: {confirm:'Are you sure you want to delete? '}, class:'btn-sm btn-danger'

model_name: book.class.name.underscore ➡︎ “book” is a block variable for each.

8. Edit application.js

application.js


//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require jquery
//= require jquery-ui/widgets/sortable // add
//= require jquery-ui/effects/effect-highlight // add
//= require bootstrap-sprockets
//= require_tree.

//= require jquery-ui/widgets/sortable ➡︎ Enable jQuery UI

//= require jquery-ui/effects/effect-highlight ➡︎ Enable drag and drop effect

9. Create/edit JavaScript file

terminal


$ touch app/assets/javascripts/table_sort.js

table_sort.js


$('.table-sortable').sortable({
  axis:'y',
  items:'.item',

  // Send the ordered data to the controller with Ajax
  update(e, ui) {
    let item = ui.item;
    let item_data = item.data();
    let params = {_method:'put' };
    params[item_data.model_name] = {row_order_position: item.index() };
    $.ajax({
      type:'POST',
      url: item_data.update_url,
      dataType:'json',
      data: params,
    });
  },

  // Fit the drag width to the table
  start(e, ui) {
    let cells, tableWidth, widthForEachCell;
    tableWidth = $(this).width();
    cells = ui.item.children('td');
    widthForEachCell = tableWidth / cells.length +'px';
    return cells.css('width', widthForEachCell);
  },

  // add effect
  stop(e, ui) {
    return ui.item.children('td').effect('highlight');
  },
});

10. Change the cursor design

application.scss


// add
.table-sortable {
  tr.item {
    cursor: row-resize;
  }
}