LoginSignup
17
17

More than 5 years have passed since last update.

vue.jsでjQuery UI Sortableを使う

Posted at

ググって見つけたコードが動いてなかったので直してみた。
コンポーネントで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をセットして移動先を判別できるようにした。
移動中のアイテムを保持するのにグローバル変数を使っているのは、
コンポーネント間でデータを共有するうまい方法がわからなかったため。
イベントを使えばもっとすっきり書けるかもしれない。

17
17
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
17
17