1日で! Vue.jsでToDo管理アプリを作ってみた件!!

# はじめに
毎週、「1週間1制作計画」っていうのをやっているのですが、今週はプチ夏休みということで、
お休みしています。と言っても、過去に作ったアプリの作り直しやポートフォリオサイトの作成の続きを今のうちに!と思って、進めて行っています。

さて、今日は、過去に1週間かけて作ったToDo管理アプリを「お手本コードを参照せずに」作ってみようと思いまして、昨日取り組んでいました。結果1日かからないくらいで制作し、デプロイまでできたので、コードを共有してみます！
**【注意】Vue.js(Vue CLI)やVuexのインストールの方法は端折っております!**

【条件】
<ul><li>最初に「ToDo管理アプリ　作り方」や「タスク管理アプリ　作り方」のようにググったり、テキストを漁ってベースとなるお手本コードを探さないこと。</li>
<li>自分の頭で考えて思いついた機能の実装方法についてはググっても良し。</li></ul>

上記のような条件を自分で作って、制作に挑戦してみました。
つまり、メインとなる機能についてお手本コードを見ながらではなく、自分の経験・知識をもとに作ってみましょうという企画です！

# 早速、必要な機能を考えてみましょう！
ToDoアプリにはどういう機能が必要になるか、まず考えてみましょう。
基本的には、以下の機能がついていれば良いかなと思います。

<ul><li>タスクの入力機能</li>
<li>入力したタスクの表示機能</li>
<li>タスクの完了・未完了をわかるように明示する機能</li>
<li>タスクを削除する機能</li>

それでは、1つずつ作っていきましょう！

# まずは、HTML部分から作っちゃおう!
App.vueファイルを編集していきます。
最初は、入力部分のHTML。

```
<template>
 <div id="app">
  <h1>ToDo Manager</h1>
  <form>
   <input type="text" required/>
   <input type="submit" value="ADD" />
  </form>
```
次に、表示部分のHTML。上記のフォームの下に続けてください。
ここできっちり、リスト表示のHTMLコード書いちゃうと、リスト表示が実行されてしまって、コードがきちんと表示されなくなってしまうので、コメントアウトで/ulで閉じるように表記しています。すいません。

```
 <ul>
  <li></li>
  <input type="button" value="DONE" />
  <input type="button" value="DELETE" />
  // ここで/ulでリストを閉じる。
 </div>
</template>
```
以上で表示するべきパーツは作り終わりました。

# store.jsを編集して機能を作っていきましょう!

store.jsファイルに移動してください。
もしなかったら、Vuexがちゃんとインストールされていないので、インストールに再度挑戦してください。
(実際、私がこのミスをやりました。スペースキーでVuexを選択しないといけないのに、普通にEnter押しちゃったんですよね...)
再度、インストールする際は、

```
npm install --save vuex
```
とすれば、利用できるようになります。この場合は、手動でstore.jsを作成し、main.jsにもstoreをインポートしてください。

きちんとVuexがインストールされて、store.jsを作れたら、早速機能を作っていきます！
まずは、表示するべきタスクを格納する場所を作っておきます。これは、入力したタスクをどんどん変数tasksの配列の要素として追加していきます。

```
export default new Vuex.Store({
    state: {
        tasks: []
    }
})
```
また、以下で示す部分に注目してみてください。

```
state: {
        tasks: [{
              title: 'パンを買う' // 説明のため、タスク追加してみた。
           }]
    }
```
最初に提示したコードでは単なる配列でしたが、実際、入力されたタスクが配列の要素として格納される時、それぞれのタスクは連想配列(オブジェクト、ハッシュ)として格納されます!このようにすると、キー名を利用していろいろな操作をすることが可能になります。

# 表示できるようにしてみよう!
ストアの状態を出せるようにしておきます。
変数tasksの状態を返すように記述します。

```
getters: {
        tasks(state) {
            return state.tasks
        }
    }
```
ここまで、store.jsで作業していましたが、一度、App.vueに戻ってください。
以下のコードを書いてください。

```
export default {
  name: 'App',
  computed: {
    tasks() {
      return this.$store.getters.tasks;
    }
  },
  data() {
    return {
      title: ''
    }
  }
}
```
store.jsでgettersっていうのを記述していましたよね?
これが、App.vueのcomputedっていう部分で役立っております。$storeのgettersから変数tasksの状態を取得して返すようになっています。

gettersについては、以下のVuexの公式サイトに詳細な説明がありますので、ご覧ください。
https://vuex.vuejs.org/ja/guide/getters.html

dataメソッドに今回、必要な要素を書いていきます。
これはつまり、タスク1つ1つの連想配列のキーとなります。
今回は、titleとしました。最初は、「''」にします。

あと、忘れちゃいけないのが、HTMLの編集!

```
<ul v-for="( task, index ) in tasks" :key="index">
      <li>
        {{ task.title }}
      </li>
      <input type="button" value="DONE" />
      <input type="button" value="DELETE" />
// ここでul閉じてね!!
```
最初に、HTML部分を作った時に、このリスト表示、ありましたよね?
ここで、一番上のulの横にv-forとかありますよね?これで、連想配列(配列)の中から要素を取り出して、繰り返し処理ができるのです!
このfor...in命令の説明は、以下の投稿でしておりますので、
「知らないぞ!?」って方は、ご参照ください!
<a href="https://qiita.com/azunas/items/1de9c2304077ef174a07">
JavaScriptの制御構文(4)〜for...in命令とfor...of命令〜</a>

あとは、Mustache構文でなんか書いてますね?
これで、仮変数taskのキー名を指定して表示しています。ここで指定したキーのプロパティが表示されます。今回では、仮変数taskのtitleを指定しているので、入力したタスクのtitleがここに表示されます。
また、v-forで繰り返し処理をする時、:keyが必要になってきます！そのため、:key="index"と指定しておきましょう。これがないとエラーが出て動いてくれません。indexと記述した場合は、仮変数として、indexを追加するのも忘れずに!

# タスクを追加できるようにしてみよう!
次は、タスクを入力して追加する機能を作っていきましょう!store.jsに戻って、次のコードを書きます。

```
mutations: {
        addTask(state, payload) {
            state.tasks.push(payload.task); //配列にpushして追加!
        }
    }
```
mutationsっていうところに、それらしき機能addTaskというものが書かれています。
引数には、stateとpayloadっていうのが渡されています。
追加の引数のことをpayloadと呼びます。

mutation、payloadsに関してもこちらの公式の説明もあります。
https://vuex.vuejs.org/ja/guide/mutations.html

追加の引数がどういう内容なのかは、App.vueで編集していきます。
App.vueに移動してください。

```
methods: {
    onclick() {
      this.$store.commit("addTask", {
        task: {
          title: this.title
        } 
      });
      this.title = '';
    }
}
```
methodsにonclickが定義されています。
これは、クリックして、タスクを追加したいからです。
中に、commitっていう某ジムのCMっぽいこと書いていますが(笑)、これは、状態の変更を行うよ〜っていう合図になるので、必ず必要です。で、どういう風に状態を変更するのかっていうのが、"addTask"のようなstore.jsのmutationsに記述した機能をpayloadという引数を利用して実行して変更するよーっていう感じです。
"addTask"の次に書いてある、以下の部分がpayloadになります。

```
                              {
        task: {
          title: this.title
        } 
      }
```
表示のところで使った仮変数taskにtitleというキーが存在する連想配列を作っています。
で、そのtitleの中身は、this.titleとなっています。これは、入力された値を格納することを表しています。

それでは、HTML部分を編集していきます！App.vueのHTML部分に移動してください。

```
<form v-on:submit.prevent="onclick">
      <div class="inputForm">
        <input type="text" id="title" v-model="title" required/>
      </div>
      <input type="submit" value="ADD" />
</form>
```
最初にHTML部分作った時に、このform、作りましたよね！
そこに色々付け足していきます。
v-onでクリックイベントが発火できるようになります。よくあるonClickと一緒ですね!
ここで、"onclick"として書かれていますが、これは、先ほど、methodsで記述したonclickを渡すことで、発火する仕組みです。

ちなみに、inputフォームの入力部分で、requiredって書いてあるのは、空欄のままでは、追加できないようにするためです。わざわざ条件分岐をしなくても、このrequiredを書くだけで、オッケーなんです!

# 削除する機能を作ってみよう!
それでは、お次は、タスクの削除機能です。store.jsに戻りましょう。

```
deleteTask(state, title) {
 let index = state.tasks.findIndex(task => task.title === title);
 state.tasks.splice(index, 1);
}
```
連想配列のインデックス番号は**findIndex**で取得します。
似たような状況で、以前、indexOfを使ってみたんですけど、こちらは連想配列では使えず、配列のみのようです。
ここで、stateの横にtitleも引数として渡されていますね！ここにきちんと利用する変数やキー名など書かないと、エラーのなります。ここでは、キー名titleを使っているので書いています。
splice(取得したインデックス番号, 1)で削除ボタンをクリックしたタスクのインデックス番号から1つ削除することができます。

通常は、spliceメソッドは置き換えの構文なのですが、要素を削除することも可能です。
以下の投稿も参考にしてみてください!
<a href="https://qiita.com/azunas/items/27eb68d2a74f5cfc84f4">
JavaScriptで、配列・連想配列から要素を削除する時に使える文法！</a>

それでは、App.vueのほうも編集していきます！

```
methods: {
    (中略)
    deleteTask(title) {
      this.$store.commit("deleteTask", title);
    }
    (中略)
  }
```
methodsの中に記述していきます。
先ほどの、store.jsの時と同じように、引数として、titleを忘れないようにします。
ここでも、commitして変更の合図を出します。その変更はdeleteTaskという処理を引数titleを使って行います。

HTML部分も編集します。

```
<ul>
 (中略)
 <input type="button" value="DELETE" 
 v-on:click="deleteTask(task.title)" />
// ここでul閉じる!
```
タスクを表示する部分に、削除ボタンを作成しましたね!そこに処理を加えていきます。
ここでも、タスク追加の時と同じように、v-onが使われています。そう、ここもクリックイベントです。
削除ボタンを押すと、deleteTaskが実行されます。methodsに書いたように、引数を忘れてはいけません。ここでは、タスクのtitleを表示した時のように、仮変数task.titleとし、titleを利用することを示します。

# 完了・未完了を明示する機能を作ってみよう!
やっとここまでたどり着きました！
store.jsに戻りましょう！mutationsに次のコードを追加してください。

```
mutations: {
 (中略)
 completeTask(state, title) {
  let index = state.tasks.findIndex(task => task.title === title);
  state.tasks[index]["title"] = "Done!!";
 }
}
```
ここでも、また削除の時と同じように、クリックされたタスクのfindIndexでインデックス番号を取得しています。
今回の場合、チェックボックスじゃ面白くないので、完了したタスクのtitle部分をDone!!という文字列に書き換えることにしました!
state.tasks[index]で、現在の状態の配列tasks(タスクが格納されている)の[求めたインデックス番号]の要素を取得し、その要素(連想配列)のキー["title"]のプロパティを、"Done!!"で書き換えています。

それでは、App.vueに移動してください。

```
methods: {
　(中略)
 completeTask(title)  {
  this.$store.commit("completeTask", title);
 }
}
```
またまた、commitしてcompleteTaskという処理をtitleという引数を使って行い、状態を変化させます。これは、先ほどの削除機能とほぼ同じなので、削除機能の項目も参考にしながら作ってみてください。

HTML部分を編集していきましょう！

```
<ul>
 (中略)
 <input type="button" value="DONE" 
 v-on:click="completeTask(task.title)" />
 (中略)//削除ボタンが入る
// ここでul閉じる!
```
ここも、先ほどの削除ボタンと同じように作業していきます。
deleteTaskかcompleteTaskかの違いだけなので、こちらは先ほどの削除機能の部分の説明を見て頂けたらと思います!
これで、全ての機能の実装が完了しました。

# 永続化しよう！
せっかくVuexを利用しているので、リロードしてもタスクが消えないように、**永続化**しちゃいましょう！
これは、本当に簡単です。ちょっとstore.jsに書き足すだけなんです！
まずは、vuex-persistedstateっていうのをインストールしてください。

```
npm install --save  'vuex-persistedstate'
```
インストールできたら、store.jsにimportします。

```
import Vue from 'vue';
import Vuex from 'vuex';
import createPersistedState from 'vuex-persistedstate'; // これ追加!
```
追加できたら、同じく、store.js内のmutationsの次に以下を記述します。

```
mutations: {
 (中略)
},
plugins: [createPersistedState()], // これ追加!
```
これだけ！これだけでもう永続化完了!

以上が今回挑戦したToDoアプリ作り直しのコードです。
なお、CSSは今回は割愛させて頂きます。

理解不足等による不備等ございましたらコメントお願い致します！
長々と失礼しました！

# 参考資料
Vuex公式
https://vuex.vuejs.org/ja/