jQueryUIを使ったD&D実装

TECH::CAMPに通い始めて2ヶ月と9日。

緊急事態宣言が長引いたので、自宅で黙々とコードを書き勉強する日々です。

 

以前jQueryUIの読み込み方法を書きましたが、

今回は実際の実装コードを記録したいと思います。

動画の貼りかたがわからないので、スクショで記録します。

 

こんな感じのToDoカンバン を用意しておき、それぞれのタスクは「taskテーブル」上で管理され、「status」カラムを持っています。(Todoなら1が入る、Doingなら2が入る、など)

また、グループごとにタスク管理をするので、

taskはgroupにネストされている状態です。

 

進捗状況が変わるとstatusカラムの数値も変更=表示位置をリロード後も変更した位置に固定 ということを実現したかったのです。

f:id:atsukofu:20200509183149p:plain

こんな感じにドラッグアンドドロップで移動します。

f:id:atsukofu:20200509183220p:plain

 

ドラッグアンドドロップ自体は簡単に実装できるのですが、

ドロップ終了時にstatusカラムの数値を変更し、リロード後に移動した位置に留めておくのが初心者にはなかなか難しかったです。

 

今回のコードはこちら。

なんだかインデントが全て消えていますね。反映されませんでした。

読みづらくて済みません。。

$(document).on('turbolinks:load', function() {
$(function() {
$('.sortable').sortable({
connectWith: '.sortable',
beforeStop: function(e, ui) {
ui.item.addClass("add-class");
},
receive: function(e, ui) {
var parent_id = $(this).data("id");
var targetTask = $('.add-class').find('.task_status_update_id');
taskId = targetTask.val();
groupId = $('.task_group_id').val();
$.ajax({
url: groupId + '/tasks/' + taskId,
type: 'PATCH',
remote: 'true',
data: {
task: {
status: parent_id
}
}
})
.done(function(data) {
location.reload();
})
.fail(function(data) {
alert('タスクを移動できませんでした');
})
}
})
})
});

 

全体を

$(document).on('turbolinks:load', function() {}

で囲っているのは、

ページ遷移時に、リロードしないとjsが読み込まれないという事態に陥ったためです。

読み込みがきちんとされるのであれば、書く必要はないと思います。

 

ここではjQueryUIの「sortable」を使用しています。

Draggable Droppableでもよかったのですが、

これだとドロップできる位置の自由度が高い為に位置が綺麗に決まらず、A型の私には満足いきませんでした。

 

sortableは、sortableメソッドを発動させた要素の子要素を動かせるようになります。

ですのでここでは、動かしたい要素(ここでは黄色いタスクの付箋のような物)の親要素にsortableというクラスを付けています。

 

また、普通にsortableを発動させるだけでは、親要素の中で並び替えるという動作しか行えませんが、

ここではsortableクラスを持つ要素間であれば行き来できるように

connectWith: '.sortable'

としています。

 

次に、ドロップした子要素(ここでは付箋と呼びます)のステータスを変更する作業です。

まず、ドロップした要素にクラス名を与えて区別できるようにします。

beforeStop: function(e, ui) {
ui.item.addClass("add-class");
}

beforeStop は、ドロップする直前に動作するようにしてくれます。

ここでは「ui」というのが、ドロップされた付箋です。

 

次に、「親要素が受け取ったら〇〇する」という命令。

receive: function(e, ui) {
var parent_id = $(this).data("id");
var targetTask = $('.add-class').find('.task_status_update_id');
taskId = targetTask.val();
groupId = $('.task_group_id').val();
$.ajax({
url: groupId + '/tasks/' + taskId,
type: 'PATCH',
remote: 'true',
data: {
task: {
status: parent_id
}
}

 

親要素が受け取ったら、

var parent_id = $(this).data("id");

どの親要素なのか(親要素のデータidは、ステータスカラムの数字と一致します)を

特定し、ステータスカラムに上書きする数字を決めます。

 

次に動かした付箋のidカラムの値を取得します。

var targetTask = $('.add-class').find('.task_status_update_id');
taskId = targetTask.val();

付箋たちにはinputタグがtype="hiddenで仕込まれており、

value="子要素のidカラムの値"が入るように仕込んでいます。

上記のように記載すると、変数taskIdに、動かした付箋のidカラムの値が入っています。

 

groupId = $('.task_group_id').val();

タスクが属するグループのidも付箋のinputタグで仕込んでいますので、クラス名を付けておけば取得できます。

 

そしてajaxでデータを送信!

$.ajax({
url: groupId + '/tasks/' + taskId,
type: 'PATCH',
remote: 'true',
data: {
task: {
status: parent_id
}
}
})

先ほど取得したグループidとタスクのidは、urlの指定で使います。

updateアクションを呼び出すので、type: 'PATCH'とします。

dataは、taskテーブルのstatusカラムのみ更新なので、先ほど取得した親要素のidに更新できるように、指定してあげます。

 

画面はこのまま変更する必要がないので、

完了後はjsonなどは受け取りません。

ただ、このままだとなぜか動かした付箋以外の付箋が移動するという謎の現象がおきました。

そこで

done(function(data) {
location.reload();
})

上記のようにページをリロードすると、なぜか直りました。

 

なかなか苦労したので、

ただの備忘録みたいになってしまっています。

初学者のためまだまだ原因不明の点も多いですが、

どんどん勉強してわかるようになりたいと思います!