Vue3 チュートリアル 3 ToDoアプリの実装練習

前回学んだVue3の基本を元に、Vue3でToDoアプリを実装していきましょう。

### ゴール

- 前回学んだ内容でVue3のToDoアプリを実装する
- できるだけ自分の力で実装を進める

https://qiita.com/TakahiRoyte/items/9033b61a0e528324da4c

## ToDoアプリ作成

今回のToDoアプリ作成では、こちらからテンプレート部分は提供します。
そこにVueでインタラクティブな処理を実装していきます。

### 準備

前回作った`App.vue`ファイルは参考にすると思うので、`Tutorial2.vue`などに名前を変えて保存しましょう。
Git管理下のファイルのファイル名を変える時は、VSCode上から実施してください。
エクスプローラーやコマンドラインからリネームするとGitの対象から外れてしまいます。
（本来はGitのコマンドを使ってやるところをVSCodeが裏でいい感じに実施してくれています）
その上で新たな`App.vue`を作って以下のコードをコピーしてください。

```vue:App.vue
<script>
export default {
  data() {
    return {}
  },
  methods: {},
}
</script>

<template>
  <h1>My ToDo App</h1>
  <input type="text" /><button>追加</button><button>完了済みを削除する</button>
  <ul>
    <li>
      <input type="checkbox" checked /><span class="todo-done"
        >Vueをマスターする</span
      >
    </li>
    <li><input type="checkbox" /><span>牛乳を買う</span></li>
    <li><input type="checkbox" /><span>家賃を払う</span></li>
  </ul>
</template>

<style>
body {
  background-color: #eee;
}

.todo-done {
  text-decoration: line-through;
}
</style>
```

画面イメージは以下の通りです：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89911/32b1a57c-a825-9d5c-cf09-60f8b2d97739.png)

### 要件

いくつかの要件を示します。
実装していく順番は自由ですが、上から下がやりやすいと思います。

- ToDoデータは完了の状態と文字列を保持しリストで表示する
- 初期表示でデータが何もない時、リストの位置に「ToDoがまだありません！」と表示する
- フォームに文字を入力し、追加ボタンを押すと文字列を元にToDoリストに追加される
- ToDoに追加したタイミングでフォームの文字列はクリアされる
- フォームに文字が未入力時に追加ボタンを押しても、アラートが表示されリストに追加されない
- ToDo毎のチェックボックスのオンオフで文字列に完了済みのラインの切り替えができる
- 完了済みを削除するボタンを押すとチェックボックスがオンになっているToDoが削除される

以上を可能な限り独力で前回のコードを参考にしながら実装してみてください。
行き詰まった場合は、上の順番通り各要件ごとにヒントのテキストとコードがあるので参考にしてみてください。

1. できるだけヒントを見ずに実装
1. 詰まったらヒントを見ながら実装
1. 分からなくなったらサンプルコードを参考に実装

## ヒント集

以下からはヒント集になります。
イメージとしては以下の順番で取り組んでみてもらえれば嬉しいです。

1. できるだけヒントを見ずに実装
1. 詰まったらヒントを見ながら実装
1. 分からなくなったらサンプルコードを参考に実装

### ToDoデータは完了の状態と文字列を保持しリストで表示する

**ヒント**

- サンプルコードを参考に`data`に仮のデータ`todos`を作ってみる
- データはオブジェクトの配列で、オブジェクトは完了状態のbooleanとToDoの文字列を持つ
- テンプレートでは`v-for`を使って仮データを配列で表示してみる
- `v-for`を適用するのは`li`要素、`v-for="todo in todos"`
- 文字列を表示するのは`{{ }}`
- この時点ではチェックボックスや完了済みラインは気にしない

**コード**

```js:script
  data() {
    return {
      todos: [
        {
          isDone: true,
          text: 'Vueをマスターする'
        },
        {
          isDone: false,
          text: '牛乳を買う'
        },
        {
          isDone: false,
          text: '家賃を払う'
        },
      ],
    }
  }
```

```html:template
    <li v-for="todo in todos">
      <input type="checkbox" /><span>{{ todo.text }}</span>
    </li>
```

### ToDoデータが何もない状態の時、リストの位置に「ToDoがまだありません！」と表示する

**ヒント**

- メッセージ表示用のHMTL要素を追加する
- `v-if`と`v-else`を使う
- `v-if`の条件はToDoデータ配列の長さが`0`の場合、`todos.length === 0`
- 最初はToDoデータがある状態で実装してみる
- 次にToDoデータを空の配列にして表示が正しくなされるか見てみる

**コード**

```js:script
  data() {
    return {
      todos: [],
    }
  },
```

```html:template
  <p v-if="todos.length === 0">ToDoがまだありません！</p>
  <ul v-else>
    <li v-for="todo in todos">
      <input type="checkbox" /><span>{{ todo.text }}</span>
    </li>
  </ul>
```

### フォームに文字を入力し、追加ボタンを押すと文字列を元にToDoリストに追加される

**ヒント**

- フォームの`input`要素の値を管理するデータを`data`内に持つ
- `v-model`を使い`input`要素と`data`の値をバインディングする
- `methods`に`addTodo`を追加する
- データのToDoの配列にインプットとバインディングされている文字列を元にオブジェクトをpushする
    - [MDNドキュメント Array.prototype.push()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/push)
- ToDoのデータはオブジェクトで、完了状態と文字列をプロパティとして持つこと
- Vueのインスタンス内でオプションのプロパティを利用する時は`this.`を忘れないこと！


**コード**

```js:script
export default {
  data() {
    return {
      newTodoText: '',
      todos: [],
    }
  },
  methods: {
    addTodo() {
      this.todos.push({
        isDone: false,
        text: this.newTodoText,
      })
    },
  },
}
```

```html:template
<input type="text" v-model="newTodoText" /><button @click="addTodo">追加</button>
```

### ToDoに追加したタイミングでフォームの文字列はクリアされる

**ヒント**

- `methods`に追加したToDoを追加するメソッドに修正を加える
- フォームとバインディングされているテキストをクリアする
- `data`内の特定の値をメソッド内で利用する時は`this.`を忘れないこと！

**コード**

```js:script
    addTodo() {
      this.todos.push({
        isDone: false,
        text: this.newTodoText,
      })
      this.newTodoText = ''
    }
```

### フォームに文字が未入力時に追加ボタンを押しても、アラートが表示されリストに追加されない

- `methods`に追加したToDoを追加するメソッドに修正を加える
- アラートはWeb標準で提供されている`alert`メソッドを使ってOK
    - [MDNドキュメント window.alert](https://developer.mozilla.org/ja/docs/Web/API/Window/alert)
- 処理を抜け出すよう、分岐を設け`return`で処理を終える

**コード**

```js:script
if(this.newTodoText === '') {
  alert('文字を入力してください')
  return 
}

// あるいは1行で簡潔に
if (!this.newTodoText) return alert('文字を入力してください')
```

### ToDo毎のチェックボックスのオンオフで文字列に完了済みのラインの切り替えができる

**ヒント**

- 文字の装飾用のCSSは`todo-done`クラスとして`<style>`に用意されている
- `class`に`v-bind`する、`v-bind`の略記法は`:xxxxx`
- `class`のバインディングの時はオブジェクトを渡し、キーをクラス名、値を真偽値(boolean)
- クラス名にハイフンが入っている場合はオブジェクトのキー名はクォーテーションマークで囲む(`'class-name'`)
- バインドするオブジェクトはこんな感じ、`{'class-name': booleanValue }`

**コード**

```html:template
      <input type="checkbox" v-model="todo.isDone" /><span
        :class="{ 'todo-done': todo.isDone }"
        >{{ todo.text }}</span
      >
```

### 完了済みを削除するボタンを押すとチェックボックスがオンになっているToDoが削除される

**ヒント**

- ボタンに対応する新たなメソッドを`methods`に追加する、名前は`clearDoneTodos`など
- Todo配列を、完了状態で未完了のものだけにフィルターした配列に置き換える
    - [MDNドキュメント Array.prototype.filter()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/filter)

**コード**

```js:script
    clearDoneTodos() {
      this.todos = this.todos.filter((todo) => !todo.isDone)
    },
```

## まとめ

実装作業はどうでしたでしょうか？
ヒントのテキストやコードを見ながらでも、実装できてコードの理解が進んでいれば順調です！
最後にサンプルとしての実装コード全体を共有するので、ご自身のコードと見比べてみてください。
必要に応じて修正してもらって、納得した形になったらGitにコミットしておきましょう。

```vue:App.vue
<script>
export default {
  data() {
    return {
      newTodoText: '',
      todos: [
        // { isDone: false, text: 'ToDoの文字列'}
      ],
    }
  },
  methods: {
    addTodo() {
      if (!this.newTodoText) return alert('文字を入力してください')
      this.todos.push({
        isDone: false,
        text: this.newTodoText,
      })
      this.newTodoText = ''
    },
    clearDoneTodos() {
      this.todos = this.todos.filter((todo) => !todo.isDone)
    },
  },
}
</script>

<template>
  <h1>My ToDo App</h1>
  <input type="text" v-model="newTodoText" /><button @click="addTodo">
    追加</button
  ><button @click="clearDoneTodos">完了済みを削除する</button>
  <p v-if="todos.length === 0">ToDoがまだありません！</p>
  <ul v-else>
    <li v-for="todo in todos">
      <input type="checkbox" v-model="todo.isDone" /><span
        :class="{ 'todo-done': todo.isDone }"
        >{{ todo.text }}</span
      >
    </li>
  </ul>
</template>

<style>
body {
  background-color: #eee;
}

.todo-done {
  text-decoration: line-through;
}
</style>
```

次回はToDoアプリをコンポーネントに分割して、再利用性を高めながら可読性もあげていきます。

https://qiita.com/TakahiRoyte/items/5230b7b3685e0ab3756d

お疲れ様でした。