写経で感覚を掴むVue

なんて事のない、TodoリストをVueで作ってみましょう。
![Jun-01-2019 11-52-26.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/231086/9f0aaa98-f7ed-823d-ca76-bf97daa9d64e.gif)



# 注意点
理論の説明は省きます。

今回の目的は写経を通じて「なんとなくわかる」状態になっていただく事です。

Vueのチュートリアルを読んで挫折してしまった方は、こちらのコードを写経していただくと、肌感覚がつかめるかと思います。


# ファイル構成

どこかしらにフォルダを作って、下記の構成でファイルを作成してみてください。
非常に単純です。

```
.
├── index.html
└── index.js
```


# STEP1 TODOを表示する

## htmlファイルの雛形を作る
とりあえずおきまりの雛形でも書いてみましょう.

```index.html
<html>
  <head>
    <meta charset="utf-8">
    <title>Vue.js App</title>
  </head>
  <body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="index.js"></script>
  </body>
</html>
```

`<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>`はVueを使えるようにするためです。
本来であれば、npmやyarn等のパッケージ管理ツールを使ってインストールして、あれこれやる必要があるのですが、Vueは初心者に優しい。。。
この一行でVueが使えるようになります。

## Vueインスタンスを定義する
TODOを表示するためのインスタンスを定義しましょう。

```index.js
var app = new Vue({
  el: '#app',
  data: {
    todos: [
      {name: "todo1", state: true},
      {name: "todo2", state: true},
      {name: "todo3", state: false},
    ],
  }
})
```

`el:`の部分でVueインスタンスを反映させる要素を指定します。
今回は`#app`、つまり`<div id="app"></div>`で囲まれた部分に対して、Vueインスタンスを反映させます。

`data:`の部分は変数名、およびその初期値を定義します。
今回は`todos`変数に3つの辞書を配列として定義しています。

## Vueインスタンスを描画する
先ほど定義したVueインスタンスを用いて、htmlファイルの方で描画してみましょう。
`index.html`の`body`タグに下記のように編集します。

```index.html
<body>
  <div id="app">
    <div v-for="(item, index) in todos" v-bind:key="index">
      <input type="checkbox" v-bind:value="index" :checked="item.state"/>{{ item.name }}
      <br>
    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script src="index.js"></script>
</body>
```

ブラウザで`index.html`を開いてみると、このようになるはずです。
![スクリーンショット 2019-06-01 12.56.46（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/231086/449022ce-56ec-6f64-75f4-edf6910fe2ba.png)

少し解説をいたします。

Vueには「[ディレクティブ](https://012-jp.vuejs.org/guide/directives.html)」という特別なトークンを定義することができます。
htmlファイル中の`v-for...`や`v-bind...`と書かれた部分がそれに当たります。

これによって、繰り返し表示を行えたり、条件分岐によって表示を制御することができるのです。

詳しくはこちらのドキュメントを参照してみてください。

- [はじめに — Vue.js](https://jp.vuejs.org/v2/guide/index.html)


# STEP2 TODOリストの各機能を実装
## チェックボックスの状態をVueにも伝える
現状でも問題なくチェックボックスは利用できますが、Vue側でもその状態を反映させましょう。

methodを追加します。

```index.js
var app = new Vue({
  el: '#app',
  data: {
    todos: [
      {name: "todo1", state: true},
      {name: "todo2", state: true},
      {name: "todo3", state: false},
    ],
  },
  methods: {
    changeState: function(index) {
      this.todos[index].state = !this.todos[index].state;
    }
  }
})
```

methodに記述するのは、そのVueインスタンスが利用できる関数です。
`changeState`により、クリックされたtodoの`state`変数が変更されます。

`changeState`を利用するために、`index.html`のファイルのcheckboxの行を下記のように修正しましょう。

```
<input type="checkbox" v-bind:value="index" :checked="item.state" v-on:click="changeState(index)"/>{{ item.name }}
```

これにより、checkboxがクリックされると、それに対応するtodoのstateが変更されます。

## 削除機能を追加
`index.js`のmethodの部分を下記のように変更しましょう。

```index.js
methods: {
  changeState: function(index) {
    this.todos[index].state = !this.todos[index].state;
  }
  deleteTodo: function(index) {
    this.todos.splice(index, 1);
  }
}
```

`index.html`に削除ボタンを追加します。

```index.html
<body>
  <div id="app">
    <div v-for="(item, index) in todos" v-bind:key="index">
      <input type="checkbox" v-bind:value="index" :checked="item.state" v-on:click="changeState(index)"/>{{ item.name }}
      <button v-on:click="deleteTodo(index)">削除する</button>
      <br>
    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script src="index.js"></script>
</body>
```

## 追加機能を追加
削除機能を追加したと同様に、`index.html`と`index.js`を編集しましょう。

最後の追加となるので、ソースコード全てを載せておきます。

```index.js
var app = new Vue({
  el: '#app',
  data: {
    todos: [
      {name: "todo1", state: true},
      {name: "todo2", state: true},
      {name: "todo3", state: false},
    ],
    message: '',
  },
  methods: {
    changeState: function(index) {
      this.todos[index].state = !this.todos[index].state;
    },
    addTodo: function() {
      this.todos.push({name: this.message, state: false});
      this.message = '';
    },
    deleteTodo: function(index) {
      this.todos.splice(index, 1);
    }
  }
})
```

```index.html
<html>
  <head>
    <meta charset="utf-8">
    <title>Vue.js App</title>
  </head>
  <body>
    <div id="app">
      <form @submit.prevent="addTodo">
        <input v-model="message" type="text">
        <button type='submit'>追加</button>
      </form>
      <div v-for="(item, index) in todos" v-bind:key="index">
          <input type="checkbox" v-bind:value="index" :checked="item.state" v-on:click="changeState(index)"/>{{ item.name }}
          <button v-on:click="deleteTodo(index)">削除する</button>
        <br>
      </div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="index.js"></script>
  </body>
</html>
```

ここまでくると、記事の最初のgifのようになっているはずです。