ググって見つけたコードが動いてなかったので直してみた。
コンポーネントでDOMの変更をモデルに反映させる。
<div id="ex-sortable">
<ul v-component="v-sortable" v-with="model: list"></ul>
</div>
<template id="sort-template">
<li v-repeat="model">{{$value}}</li>
</template>
Vue.component('v-sortable', {
template: "#sort-template",
ready: function() {
var self = this;
$(this.$el).sortable({
opacity: 0.6,
start: function(e, ui) {
ui.item.data('from', ui.item.index());
},
update: function(e, ui) {
var from = ui.item.data('from'),
to = ui.item.index();
self.model.splice(to, 0, self.model.splice(from, 1)[0]);
}
});
}
});
new Vue({
el: '#ex-sortable',
data: {
list: ['Item 1', 'Item 2', 'Item 3', 'Item 4']
}
});
ドラッグ開始時に、uiオブジェクトに移動前のインデックスを保存し、
完了後にそれを取得して、モデルも後追いで位置を入れ替える。
readyではなくcreatedだと要素が描画されていないためsortableが有効にならない点に注意。
2つのリスト間を相互に移動させる場合
<div id="ex-sortable">
<div>
<ul class="sortable" v-component="v-sortable" v-with="model: list1"></ul>
<pre>{{list1 | json}}</pre>
</div>
<div>
<ul class="sortable" v-component="v-sortable" v-with="model: list2"></ul>
<pre>{{list2 | json}}</pre>
</div>
</div>
<template id="sort-template">
<li v-repeat="model">{{$value}}</li>
</template>
var moving;
Vue.component('v-sortable', {
template: "#sort-template",
ready: function() {
var self = this;
$(this.$el).sortable({
opacity: 0.6,
connectWith: '.sortable',
start: function(e, ui) {
ui.item.data('from', ui.item.index());
},
update: function(e, ui) {
var from = ui.item.data('from'),
to = ui.item.index();
if (this !== ui.item.parent()[0]) { // move from
moving = self.model.splice(from, 1)[0];
} else if (from < 0) { // move to
self.model.splice(to, 0, moving);
} else { // same
self.model.splice(to, 0, self.model.splice(from, 1)[0]);
}
},
receive: function(e, ui) {
ui.item.data('from', -1);
}
});
}
});
new Vue({
el: '#ex-sortable',
data: {
list1: ['Item 1', 'Item 2', 'Item 3'],
list2: ['Item 4', 'Item 5', 'Item 6', 'Item 7']
}
});
別のリストへの移動の場合はupdateコールバックが2回走るため、
インデックスのかわりに-1をセットして移動先を判別できるようにした。
移動中のアイテムを保持するのにグローバル変数を使っているのは、
コンポーネント間でデータを共有するうまい方法がわからなかったため。
イベントを使えばもっとすっきり書けるかもしれない。