株式会社グローバルゲート公式ブログ

Vue.jsとVue.Draggableを使ったカンバン式タスク管理システムを作ってみよう

こんにちは。株式会社グローバルゲート制作部のモーリーです。 
ヨーロッパではサッカーのチャンピオンズリーグ、自転車のジロ・デ・イタリア、テニスの全仏オープンと観客数を抑えながらも大きなスポーツイベントが立て続けに開催されました。 
日本で今夏開催される予定の某イベントは果たしてどうなるのか。。。つつがなく終わることを祈ります。




さて、今回はVue.jsとVue.draggableというライブラリを使った簡単なカンバン式タスク管理システムの作り方を実例を交えてご紹介します。

実際に動作するデモも用意しました。
 入力内容はlocalStrageにしか保存されませんので気軽に試してみてください。

カンバンとは?

カンバンはタスク管理方法の一種で、やるべきことをグループ化し、更に細分化してリスト化します。タスクが終わったら完了グループに移動するかリストから削除します。ホワイトボードに付箋でやることを貼り付けて終わったら剥がす、と考えると分かりやすいかもしれません。 

ちなみに英語でも「Kanban」と呼ばれているようです。

必要な機能 

それでは、実際に作成してみましょう。 
このタスク管理システムを作成するためには、次の機能が必要です。 

1.タスクの読み込みと保存 
2.タスクグループの追加・削除 
3.タスクの追加・削除 
4.タスクの入れ替え 
5.タスクの保存 


これを順番に作成していきます。

1.タスクの読み込みと保存 

タスクのデータはlocalStrageに保存することとします。 
きちんとやるならサーバ上のDBに保存するべきですが…今後の課題とします。 

タスクのデータは次のようなオブジェクトで保持します。 
tasks配下にタスク名(name)とタスク詳細(task)があり、そのtask配下にタスク(name)があるという構成です。

task:[{ 
   "name": "買うもの", 
   "task": [{ 
       "name": "キッチンハイター\nハンドソープ" 
   }, { 
       "name": "豆腐\nなめこ\nウェイパー" 
   }] 
}, { 
   "name": "帰宅後やること", 
   "task": [{ 
       "name": "空気清浄機のフィルター掃除\nエアコンフィルター掃除" 
   }, { 
       "name": "水道代の支払い" 
   }, { 
       "name": "メルカリ発送" 
   }] 
}, { 
   "name": "", 
   "task": [] 
}]

localStrageにデータがないときはダミーで空のタスクを読み込み、データがあるときは読み込んだlocalStageのデータを代入します。この処理をmounted()で実行します。 

    mounted() { 
       const vm = this; 
       let savedTasks = localStorage.getItem('tasks'); 
       if (savedTasks) { 
           savedTasks = JSON.parse(savedTasks) 
       } else { 
           savedTasks = [{ 
               name: '', 
               task: [] 
           }] 
       } 
       vm.$set(vm, 'tasks', savedTasks); 
   },

2.タスクグループの追加・削除 

次にタスクグループの増減を行う処理を書きます。 
…と言っても、タスクがオブジェクトになっているという点を理解すれば難しいことではありません。 
そのオブジェクトを追加したり削除したりというだけでタスクグループの増減は簡単にできます。 

addGroup() { 
   const vm = this; 
   vm.tasks.push({ 
       name: '', 
       task: [] 
   }) 
}, 
delGroup(index) { 
   const vm = this; 
   vm.tasks.splice(index, 1); 
}

タスクグループの名前はv-modelを使えば簡単に変更対象とすることができます。 

<div class="mb-3"><input type="text" class="form-control fw-bold" v-model="item.name" 
   placeholder="グループ名を入力"> 
<div class="text-end mt-2"> 
   <button class="btn btn-sm btn-outline-danger" 
       v-on:click="delGroup(index)">このタスクグループを削除</button> 
</div> 
</div> 

3.タスクの追加・削除 

グループ配下のタスクの増減も同様です。 

addTask(item) { 
   const vm = this; 
   item.task.push({ 
       name: '' 
   }) 
}, 
delTask(item, index) { 
   const vm = this; 
   item.splice(index, 1); 
},

4.タスクの入れ替え 

ここまででも最低限の機能はできましたが、もう一歩踏み込んでドラッグ&ドロップによる操作をやってみましょう。 
Vue.Draggableというライブラリを使用し、ドラッグによる順番変更やグループ間の移動をやってみます。 

Vue.Draggableはv-forのループを<draggable>で囲むだけで、簡単にドラッグによる順番変更機能を追加することができます。 
タスクグループとタスクそれぞれをドラッグ操作可能とするために、<draggable>が2重になる点に注意してください。 

<draggable class="row flex-nowrap w-100" ghost-class="ghost" style="overflow: auto;" @end="saveCurrent"> 

<div class="col-3 items py-4" v-for="(item,index) in tasks" :key="'task-' + index" 
   v-on:mouseenter="addClass($event)" v-on:mouseleave="removeClass($event)"> 
   <div class="mb-3"><input type="text" class="form-control fw-bold" v-model="item.name" 
           placeholder="グループ名を入力"> 
       <div class="text-end mt-2"> 
           <button class="btn btn-sm btn-outline-danger" 
               v-on:click="delGroup(index)">このタスクグループを削除</button> 
       </div> 
   </div> 
   <div class="bg-white rounded"> 
       <draggable class="items" ghost-class="ghost" :list="item.task" group="tasks" @end="saveCurrent"> 
           <div class="item p-3" v-for="(item2,index2) in item.task" :key="'item-' + index2" 
               v-on:mouseenter="addClass($event)" v-on:mouseleave="removeClass($event)"> 
               <textarea class="form-control" style="height: 100px;" v-model="item2.name" 
                   placeholder="タスクを入力"></textarea> 
               <div class="text-end mt-2"> 
                   <button class="btn btn-sm btn-outline-danger" 
                       v-on:click="delTask(item.task,index2)">このタスクを削除</button> 
               </div> 
           </div> 
       </draggable> 
       <div class="p-3"> 
           <button class="btn w-100 btn-secondary" v-on:click="addTask(item)">タスクの追加</button> 
       </div> 
   </div> 
</div> 
</draggable> 

5.タスクの保存 

タスク管理システムですから、ここで入力した内容は保存しないといけません。 
Vueのwatch機能を使い、タスクオブジェクトに変更があったときにlocalStrageに保存するという処理を加えます。 

tasksは多階層のオブジェクトであるため、deep:trueとすることで配下要素の変更も監視して処理を実行することができます。 

watch: { 
tasks: { 
    handler() { 
        const vm = this; 
        vm.saveCurrent(); 
    }, 
    deep: true 

},

ソースコート全文はGithubレポジトリをご参照ください。

できあがり! 

それぞれの処理はごく簡単なものですが、その簡単な処理を組み合わせることでちょっとしたシステムを構築することができました。 

一見すると難しそうなシステムもアイデア次第で簡単に実現できることもITの世界ではよくあります(逆に簡単そうなシステムが難しかったりもしますが…)。
ほしいもの、つくりたいものがございましたらぜひご相談ください。

【関連記事】

ご相談・お問い合わせ

当社サービスについてのお問い合わせは下記までご連絡下さい。

お電話でのお問い合わせ

06-6121-7581 / 03-6415-8161