はじめに
HTMLのdraggable属性をRailsと組み合わせたサンプルを作成したのでメモとして残しておきます。
参考にしたサイト
サンプルの作成にあたって下記のサイトを参考にさせていただきました。
意外と知らないHTML5 API - Drag & Drop APIとは | CodeGrid
[HTML5] Drag & Drop API おさらい 「DOM 要素の DnD」 | Developers.IO
jQuery内からRailsのActionを叩く - Qiita
やりたかったこと
テーブル内の行をドラッグ&ドロップすることで、別のテーブルにデータが追加される
テーブル
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を利用できる状態にする
gem 'jquery-rails'
//= require jquery
Sampleコントローラを作成する
$ rails g controller sample index add
get 'sample/index'
post 'sample/add'
テーブルを作成する
rails g scaffold User name:string
rails g scaffold SelectedUser user_id:integer
rake db:migrate
Controller
データを一覧表示するView用のメソッドとデータ追加するメソッドを作成
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さんのサイトに掲載されているサンプルに少しだけ処理を加えたものになっております。
※ドラッグ&ドロップ後の処理がおそまつですが、そこはおいおい修正することに・・・
<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>