Cwらぼっちゃ - IT技術研究ノート -

IT技術に関する記事を掲載します。

Vue.jsを使ってTODOリストを作ろう!

前回の記事【Vue.jsってなに?】ではVueの環境構築から簡単にコードを触るところまでを書きました。
今回は、簡単なTODOリストを作りながらVueの機能をもう少し詳しく見ていきます。

作り始める前に

前回の記事でもお話したように、コマンド:vue createでプロジェクトを作成すると、App.vueとHelloWorld.vueの2つのvueファイルができます。
これらはコンポーネントと呼ばれ、親コンポーネントApp.vue、子コンポーネントHelloWorld.vueコンポーネントがネストした状態になっています。
画面上では、以下のように表示されています。

画面の確認方法:
powershellの場合
1. プロジェクトファイルがあるディレクトリまで行き、yarn serve コマンドを入力する。
2. サーバが起動したら http://localhost:8080/ にアクセスする。

VSCodeを使っている場合、ターミナルでyarn serveコマンドを入力すればあとは上記と同じ動作で画面確認ができます。
(個人的にはpowershellよりターミナルでやったほうがディレクトリ移動がない分スムーズで良かったです)

f:id:CwLab:20190916220047p:plain
コンポーネントがネストしている状態

HelloWorld.vueのHTML部分、 <template></template>内はすべて不要なので消します。
タイトルを書きたいのでh1タグは残しておきます。
そうすると画面上ではこうなります。

f:id:CwLab:20190916215825p:plain

この枠線の中にTODOリストを作っていきます。
これでTODOリストを作る準備は完了です。

TODOリストを作ってみよう

今回実装する機能は以下の通りです。
・タスクを追加する
・タスクを削除する
・チェックがついたタスクを一括削除する機能

タスクを追加する

タスクを追加する機能です。
ADDボタンを押下するとタスクを追加(下に表示)する機能を実装します。

その前に、前回の記事【Vue.jsってなに?】でもやったテキストボックスに入力した文字列がそのまま下に表示される処理を復習します。
-コード-

<template>
  <div>
    <h1>ToDoList</h1>
    <div>
      <label>
        <input type="textarea" v-model="newTask" />
      </label>
      <p>{{ newTask }}</p>
    </div>
  </div>
</template>

<script lang="ts">
export default class HelloWorld extends Vue {
  private newTask: string  = '';
}
</script>

-仕組み-
v-modelによる双方向データバインディングによって、テキストボックスとこのクラスのプロパティであるnewTaskが連動しており、newTaskの文字列を変更できます。pタグの中でnewTaskを呼び出すことによってテキストボックスに文字列が入力された際にリアルタイムで文字列が変更されるのを確認できます。

それでは、タスク追加機能を作っていきます。
まずはADDボタンを作成します。
ボタンにはv-on:clickを設定しておきます。イベントハンドラ名はaddTaskとします。

イベントハンドラとは、イベントが発生したときに呼び出される処理のこと。
vue.jsの場合、v-on ディレクティブを使うことで、DOM イベントの購読、イベント発火時の JavaScript の実行が可能となる。詳細

<button v-on:click="addTask">ADD</button>

次に、TODOのタスクを格納する配列を用意して、ADDボタンが押された時に呼ばれるaddTaskメソッドで、テキストボックスに入力されている値とデータバインディングされているnewTaskの値を配列に追加するイベントハンドラを設定します。
タスク1件が持つデータは、タスクの名前と完了・未完了も後ほど管理する処理を書くため、オブジェクトとして配列に追加していきます。

<script lang="ts">
export default class HelloWorld extends Vue {
  private newTask: string  = '';
  private id: number = 1; // numberは53ビット整数。浮動小数点数も格納出来る。
  private tasks: {id: number; title: string}[] = []; // TODOのタスクを格納する配列

  private addTask() {
    if (this.newTask !== '') {
      this.tasks.push({
        id: this.id++,
        title: this.newTask,
      });
    }
    this.newTask = '';
  }
}
</script>

newTask(テキストボックスの中)が空文字でないとき、idを+1してnewTaskをpush()関数を使って配列に追加するようにしました。

push()関数:配列taskssの末尾にtask(=newTask)を追加する

そして、this.newTask = ' ';でテキストボックスの中を空にしています。

次に、配列に格納したデータをリスト表示させる部分を作っていきたいと思います。
v-forディレクティブを利用します。

<ul v-for="task in tasks" :key="task.id">
  <li>
    <span>{{ task.title }}</span>
  </li>
</ul>

ulタグを1つ作り、そのulタグにv-forディレクティブを記述します。
そして、liタグの中にspan要素を作り、マスタッシュ構文でタスク名を表示させます。こんな感じですね。

マスタッシュ構文とは、Vue.jsにおける基本的なテキスト展開の方法のこと。
HTML内で{{ プロパティ名 }}で記述する。詳細

これでタスクを追加する機能の完成です。
お疲れさまでした。

タスクを削除する機能

続いてタスクを削除する機能です。
DELETEボタンを押下するとタスクを削除する機能を実装します。

まずはタスクの右側にDELETEボタンが表示されるようにします。

<div>
  <ul v-for="task in tasks" :key="task.id">
    <li>
      <span>{{ task.title }}</span>
      <button>DELETE</button>
    </li>
  </ul>
</div>

次に、ボタンにv-on:clickを設定します。イベントハンドラ名はdeleteTaskとします。

<button v-on:click="deleteTask">DELETE</button>

イベントハンドラを設定していきましょう。
DELETEボタンはタスクの数だけ存在するようになります。ですので、どのボタンに対して動作させるのかを判断させなければなりません。

そのために、deleteTaskメソッドの引数に配列tasksのindexを渡すようにします。

<script>
  private deleteTask(index)) {

  }
</script>

HTML側でも記述が必要になります。
削除ボタンのクリックイベントでdeleteTaskメソッドを呼ぶ時に配列のindexを渡すようにします。

<button v-on:click="deleteItem(index)">DELETE</button>

このdeleteTask(index)のindexがどこで取れるのかというと、
<ul v-for="task in tasks">部分になります。
ですのでこの部分を、

<ul v-for="(task,index) in tasks" :key="task.id">
  <li>***</li>
</ul>

とします。
第一引数(task)はValue、第二引数(index)はKeyが取得できます。
そして、spliceを使って配列のデータを削除させます。

<script>
private deleteTask(index) {
  this.tasks.splice(index, 1)
}
</script>

第一引数(index)が削除を始める配列のインデックス、第二引数(この場合1)は削除をする個数となります。つまり、index番目から1つ要素を削除する、という意味になります。

これで、タスクを削除する機能の完成になります。
お疲れさまでした。

チェックがついたタスクを一括削除する機能

続いてはチェックがついているタスクをまとめて削除する機能を実装します。

まず、それぞれのタスクにチェックボックスを設置します。
また、今回は一括削除する機能のためDELETEボタンは一つあればいいので、ulタグの外に出します。

<button v-on:click="deleteTask(index)">DELETE</button>

<ul v-for="task in tasks" :key="task.id">
    <li>
      <label>
        <input type="checkbox" />
        <span>{{ task.title }}</span>
      </label>                
    </li>
  </ul>

次に、オブジェクトtasksにタスクの完了・未完了を管理する部分を追加します。

<script>
  private tasks: {id: number; title: string, isChecked: boolean}[] = []; // ←isCheckedを追記

  private addTask() {
      if (this.newTask !== '') {
        this.tasks.push({
          id: this.id++,
          title: this.newTask,
          isChecked: false, // ←追記
        });
      }
      this.newTask = '';
    }
</script>

isCheckedがfalseの場合は作業か完了していない、trueの場合にはタスクが完了している、と判断します。
次はv-modelディレクティブを使って、先ほど設置したチェックボックスとisCheckedを双方向データバインディングします。
HTML側の<input>タグにv-modelを追記していきます。

<input type="checkbox" v-model="task.isChecked" />

こうすることによって、チェックボックスとisCheckedと双方向バインディングすることができます。
次に、チェックが付いている(完了した)タスクを削除するイベントハンドラを設定します。
先程使ったdeleteTaskメソッドを修正していきます。

まず、今回は一括削除ですので引数はいりません。
そして、isCheckedがfalse、つまりチェックが付いていない (完了していない) タスクのみ表示するようにします。

<button v-on:click="deleteTask">DELETE</button>

<script>
private deleteItem() {
  this.tasks = this.tasks.filter(function(task) {
    return task.isChecked === false;
  });
}
</script>

filter関数でisCheckedがfalseのものを探し出し、新しい配列(tasks)にしてreturnしています。
これでチェックが付いているタスクを一括削除する機能が完成しました。
お疲れさまでした。

完成したものがこちら!

f:id:CwLab:20190919000444p:plain

スタイルなどはつけていないのでシンプルイズザベストな仕上がりとなっております。
スタイルをつけることによってチェックボックスをつけたときに取り消し線を入れることもでき、よりTODOリストっぽくなるので
ぜひぜひお試しください。
ー完了したタスクに取り消し線を入れる方法

最後に

以上で3つの機能すべてを作り終えました。
まだまだ基礎の基礎なので、奥の深さを感じますが、導入コストが低いだけあって、スムーズに学習を始められました。

TODOリストは様々なディレクティブを活用できるのでVue.jsを学ぶにあたって最適な教材です。
ぜひ他の機能も追加してみてください。

参考文献