LoginSignup
5
10

More than 5 years have passed since last update.

テーブル内の行のドラッグ&ドロップ操作でデータを追加する(Rails+HTML+JavaScript)

Last updated at Posted at 2017-08-05

はじめに

HTMLのdraggable属性をRailsと組み合わせたサンプルを作成したのでメモとして残しておきます。

参考にしたサイト

サンプルの作成にあたって下記のサイトを参考にさせていただきました。

意外と知らないHTML5 API - Drag & Drop APIとは | CodeGrid
[HTML5] Drag & Drop API おさらい 「DOM 要素の DnD」 | Developers.IO
jQuery内からRailsのActionを叩く - Qiita

やりたかったこと

テーブル内の行をドラッグ&ドロップすることで、別のテーブルにデータが追加される

draggable1.png → draggable2.png

テーブル

Userテーブル

カラム名 説明
name string ユーザ名

スクリーンショット撮影時のデータ

id name
1 Yamada Taro
2 Yamada Jiro
3 Yamada Saburo

SelectedUserテーブル

カラム名 説明
user_id integer UserIDを格納する

スクリーンショット撮影時のデータ
操作前

id user_id
1 1

操作後

id user_id
1 1
2 2

準備

jqueryを利用できる状態にする

Gemfile
gem 'jquery-rails'
app/assets/javascripts/application.js
//= require jquery

Sampleコントローラを作成する

command
$ rails g controller sample index add
routes.rb
get 'sample/index'
post 'sample/add'

テーブルを作成する

command
rails g scaffold User name:string
rails g scaffold SelectedUser user_id:integer
rake db:migrate

Controller

データを一覧表示するView用のメソッドとデータ追加するメソッドを作成

app/controllers/sample_controller.rb
class SampleController < ApplicationController

  protect_from_forgery except: [:add]

  def index
    @users = User.all
    @selected_users = SelectedUser.all
  end

  def add
    selected_user = SelectedUser.new
    selected_user.user_id = params[:id]

    if selected_user.save
      #head 201
      user = User.find(selected_user.user_id)
      hash = {id: user.id, name: user.name}
      require 'json'
      render :json => hash.to_json
    else
      head 500
    end

  end
end

View

ドラッグ&ドロップ部分の実装はCodeGridさんのサイトに掲載されているサンプルに少しだけ処理を加えたものになっております。

※ドラッグ&ドロップ後の処理がおそまつですが、そこはおいおい修正することに・・・

app/views/sample/index.html.erb
<h1>Draggable Sample</h1>

<hr>

<!-- SelectedUser一覧をリスト表示 -->
<h2>SelectedUser</h2>

<table border="1" width="300">
  <tr>
    <th>id</th>
    <th>name</th>
  </tr>
  <% Array(@selected_users).each do |selected_user| %>
      <tr>
        <td><%= selected_user.user_id %></td>
        <td><%= User.find(selected_user.user_id).name %></td>
      </tr>
  <% end %>
  <tr>
    <!-- ドロップエリア -->
    <td colspan="3" id="dropzone" class="dropzone"></td>
  </tr>
</table>

<br>

<!-- User一覧をリスト表示 -->
<h2>User</h2>

<table id="list" border="1" width="300">
  <tr>
    <th>id</th>
    <th>name</th>
  </tr>
  <% Array(@users).each do |user| %>
      <!-- rowにドラッグ可能(draggable)属性を指定 -->
      <tr class="item" draggable="true" id="<%= user.id %>">
        <td><%= user.id %></td>
        <td><%= user.name %></td>
      </tr>
  <% end %>
</table>


<script>

    $(function () {

        // dropzoneの表示テキストを初期化
        initDropzone();

        // listテーブルのitem行が操作された時のリスナーを設定
        items = document.getElementById('list').getElementsByClassName('item');

        Array.prototype.forEach.call(items, function (item) {
            $(item).on('dragstart', onDragStart);
            $(item).on('dragend', onDragEnd);
        });

        // dropzoneのリスナーを設定
        var $dropzone = $('#dropzone')
            .on('dragover', onDragOver)
            .on('dragenter', onDragEnter)
            .on('dragleave', onDragLeave)
            .on('drop', onDrop);


        // dropzoneの表示テキストを指定
        function initDropzone() {
            $('#dropzone').text("ここにドロップできます。");
        }

        function startDropzone() {
            $('#dropzone').text("ドラッグ中。");
        }

        function endDropzone(name) {
            $('#dropzone').text(name + "をドロップしました。");
        }

        // ドロップ時の処理
        // (1) ドロップされた行のidをPOSTする
        // (2) 成功したらリダイレクトする
        // (3) 失敗したらダイアログを表示する
        function doAction(id) {
            $.ajax({
                url: "<%=  sample_add_path  %>",
                type: "POST",
                data: {
                    id: id
                },
                dataType: "html",
                success: function (data) {
                    //alert("success");

                    // dataにドラッグ&ドロップした
                    // Userのid, nameがjson形式で
                    // 渡される
                    // console.log(data);
                    // {"id":1,"name":"Yamada Taro"}

                    // 暫定的にページを再読込
                    location.href = "<%= sample_index_path %>"
                },
                error: function (data) {
                    alert("errror");
                }
            });
        }

        // ドラッグ&ドロップ操作
        function onDragStart(e) {
            var id = e.originalEvent.target.id;
            var name = e.originalEvent.target.cells[1].innerHTML;
            e.originalEvent.dataTransfer.setData('id', id);
            e.originalEvent.dataTransfer.setData('name', name);
            addDraggingEffect();
            startDropzone();
        }

        function onDragEnter(e) {
            addEnterEffect();
        }

        function onDragLeave(e) {
            removeEnterEffect();
        }

        function onDragOver(e) {
            e.preventDefault();
        }

        function onDragEnd(e) {
            resetAllEffect();
        }

        function onDrop(e) {
            e.preventDefault();
            var id = e.originalEvent.dataTransfer.getData('id');
            var name = e.originalEvent.dataTransfer.getData('name');
            endDropzone(name);
            doAction(id);
        }

        function addDraggingEffect() {
            $dropzone.addClass('dragging');
        }

        function removeDraggingEffect() {
            $dropzone.removeClass('dragging');
            initDropzone();
        }

        function addEnterEffect() {
            $dropzone.addClass('dragenter');
        }

        function removeEnterEffect() {
            $dropzone.removeClass('dragenter');
        }

        function resetAllEffect(e) {
            removeDraggingEffect();
            removeEnterEffect();
        }

    });

</script>


<style>

  .dropzone p {
    margin: 0;
    padding: 0;
    background-color: #0000E9;
  }

  .dropzone.small {
    height: 40px;
  }

  .dropzone {
    border: 1px solid #555;
    margin-top: 5px;
    /*width: 246px;*/
    min-height: 100px;
    font-size: 12px;

    text-align: center;
    line-height: 100px;

    color: gray;
    background-color: ghostwhite;
    border-color: gray;
  }

  .dragging {
    color: rgba(0, 0, 0, .4);
    background-color: rgba(0, 0, 255, .1);
    border-color: rgba(0, 0, 255, .3);
  }

  .dragging:after {
  }

  .dragenter {
    border-color: rgba(255, 0, 0, .3);
    background-color: rgba(255, 0, 0, .1);
  }

</style>
5
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
10