knockout以外のライブラリやプラグインからイベントを追加することもできます。
その1で紹介したbindingHandlersに追加する方法で実装します。
※必要なjQueryUIのライブラリを読み込むこと。
// ここでドラッグ&ドロップする要素を一時的に保持します
var _dragged;
ko.bindingHandlers.draggable = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var dragElement = $(element),
dragOptions = {
// オプションの条件は各自調整
helper: 'clone',
revert: true,
revertDuration: 0,
start: function() {
_dragged = ko.utils.unwarapObservable(valueAccessor().value);
},
cursor: 'default'
};
dragElement.draggable(dragOptions).disableSelection();
}
};
ko.bindingsHandlers.droppable = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var dropElement = $(element),
dropOptions = {
drop: function (event, ui) {
if (_dragged) {
valueAccessor().value(_dragged);
_dragged = null;
}
}
};
dropElement.droppable(dropOptions);
}
};
var viewModel = {
// hogehoge...
itemList: ko.observableArray([
{ key: 'poppo', value: 'Yukio Hatoyama' },
{ key: 'kan', value: 'Naoto Kan' },
{ key: 'dojo', value: 'Noda San' }
]),
dropList: ko.observableArray([]),
addItem: function (data, event) {
this.dropList.push(data);
}
}
ko.applyBindings(viewModel);
大体わかるよね。
あまり美しくないやり方だと思っているけど、_draggedという変数を用意して、そこに一時的に値を保持させています。
HTML側の以下のように書きます。
<h2>Item List</h2>
<ul data-bind="foreach: itemList">
<li data-bind="draggable: { value: $data }"><span data-bind="text: value"></span></li>
</ul>
<div data-bind="droppable: { value: addItem }">
dropZone
</div>
<h2>Drop List</h2>
<ul data-bind="foreach: dropList">
<li><span data-bind="text: value"></span></li>
</ul>
この例ではリストからリストに直接渡すのではなく、別のドロップゾーンにドラッグする仕様になってます。
ドロップゾーンの droppable~
という部分をDropListの方に追記すれば、直接ドロップすることもできます。
Item Listのdraggableに $data
という記述がありますが、これはそのスコープのデータそのものです。
通常のJSで言うところの this
です。
大体こんな感じです。
ドラッグ&ドロップと言えばsortableもあるかと気になる所ですが、こっちはknockoutに対応したバージョンが既にあるので、そちらを使うと楽でしょう。
https://github.com/rniemeyer/knockout-sortable
上記ライブラリを読み込んでおけば、 foreach
の代わりに sortable
を使用することで簡単に並び替えを実現できます。
<h2>Drop List</h2>
<ul data-bind="sortable: dropList">
<li><span data-bind="text: value"></span></li>
</ul>
サクッと動くのは良いのですが、オプションの指定方法がわからなくて困りました。
指定する場合はdata-bindの一連の流れで書き込むと有効になります。
<h2>Drop List</h2>
<ul data-bind="sortable: { data:dropList, options: {axis:'y', cancel:'.key-detail'}">
<li>
<div data-bind="text: value"></div>
<div class="key-detail">key: <span data-bind="text: key"></span></div>
</li>
</ul>
optionsに必要な指定をカンマ区切りのオブジェクトで渡せば有効になります。
これは使えます。