はじめに
こちらは やさしめ Vue.js チュートリアル(1) ~ Vue CLI でプロジェクト作成と Linter の設定 の続きとなります。
今回はデータとイベントの制御について学びます 
ハンズオン
1. 余計な記述の削除とタイトルの追加
まずは今回のハンズオン向けに初期状態の余計な記述を削除しましょう。
- 
HelloWorld.vueを削除
- 
App.vue修正- タイトルを追加
- HelloWorld コンポーネントを利用している箇所を削除
- スタイルタグの中身を削除
 
以下のようになります。
<template>
  <div id="app">
    <h1>Lovely Vue</h1>
  </div>
</template>
<script>
export default {
  name: "App"
};
</script>
<style></style>
 動作
 動作 
真っ白な背景にタイトルだけが表示されていれば OK です 
 ソースコード
 ソースコード 
https://github.com/taiju59/lovely-vue/commit/b656c9366307fed7a17db2721265ff0e463a22f4
2. 宣言的レンダリング
スクリプト側で定義したデータをtemplate側で表示する方法を学びます。
まず、スクリプト側で data として message を定義
  export default {
-   name: "App"
+   name: "App",
+   data: () => ({
+     message: "Spark joy!"
+   })
  };
テンプレート側で {{ message }} と記述して表示させます。
  <div id="app">
    <h1>Lovely Vue</h1>
+   {{ message }}
  </div>
 動作
 動作 
「Lovely Vue」の下に「Spark joy!」と表示されていれば OK です 
 ソースコード
 ソースコード 
https://github.com/taiju59/lovely-vue/commit/275e6229a2aa10400389679d39f7b8929bc7d129
 ポイント
 ポイント 
- 
dataで変数を宣言- ここでは詳しく説明しませんが、dataは関数でなければなりません1
 
- ここでは詳しく説明しませんが、
- 
{{ }}で囲めば変数を展開できる
3. ユーザー入力の制御
ユーザーの入力に応じて表示内容を変更してみましょう。
イベントリスナー登録(v-on)
テンプレート側にイベントリスナーを登録しましょう。
input 要素の input イベントが走ったときに onInput という名前の関数を呼ぶようにしておきます。
  <div id="app">
    <h1>Lovely Vue</h1>
+   <input @input="onInput" />
+   <br />
    {{ message }}
  </div>
このままでは onInput という関数を呼ぼうとして存在せずエラーとなってしまうので、
スクリプト側に呼ばれるメソッドである onInput を定義しましょう。
この中で、入力内容を取得し message を上書きします。
  export default {
    name: "App",
    data: () => ({
      message: "Spark joy!"
-   })
+   }),
+   methods: {
+     onInput(event) {
+       const text = event.target.value;
+       this.message = text;
+     }
+   }
};
 動作
 動作 
入力欄に入力した内容がその下に表示されるようになっていれば OK です 
 ソースコード
 ソースコード 
https://github.com/taiju59/lovely-vue/commit/802a6b6d02d1d2f4f9dede8711715314e5af7eec
 ポイント
 ポイント 
- 
@inputでインプットイベントと関数を紐付け2- 
@inputの@は生の JavaScript でいうaddEventListenerと考えておけば OK
 
- 
- 
methodsで関数を宣言
- スクリプト内では thisのプロパティとしてdataにアクセスできる
双方向バインディング(v-model)
画面表示時は入力欄の中は空なのに、
入力欄の下には「Spark joy!」が表示されていますね。
なぜこれが起きているのか?というと、
入力内容 => message という方向には連動していますが、
message => 入力内容 という方向には連動していないからです。
これを連動させるため、双方向バインディング(v-model)を用います。3
テンプレート部分の input の記述を以下のように変更しましょう。
- <input @input="onInput" />
+ <input v-model="message" />
いらなくなっちゃったので、スクリプト部分の onInput は削除しましょう。
  export default {
    name: "App",
    data: () => ({
      message: "Spark joy!"
-   }),
+   })
-   methods: {
-     spark(event) {
-       const text = event.target.value;
-       this.message = text;
-     }
-   }
};
 動作
 動作 
表示時に入力欄に「Spark joy!」が入っていて、且つ、
入力内容に連動してその下の文字列が変化するはずです 
 ソースコード
 ソースコード 
https://github.com/taiju59/lovely-vue/commit/c312b2c7413630980ef588c0e8bf5722c977759f
 ポイント
 ポイント 
- 
v-modelで入力内容と変数を双方向バインディングできる
4. ループ( v-for )
いわゆる for 文です。
v-for を使うことでループでDOMを追加できます。
まず、スクリプト部分で data に todos という変数を定義します。
  data: () => ({
+   todos: [
+     { id: 1, text: "ときめき" }
+   ],
    message: "Spark joy!"
  })
テンプレート部分で v-for を使ってループで表示させます。
  <input v-model="message" />
  <br />
+ <ul>
+   <li v-for="todo in todos" :key="todo.id" class="task-card">
+     {{ todo.text }}
+   </li>
+ </ul>
class は普通に HTML 属性としての class になります。
スタイル部分にそれっぽいスタイルも追加しておきましょう。
- <style></style>
+ <style>
+ .task-card {
+ border: solid 1px;
+ margin: 5px;
+ padding: 2px;
+ width: 250px;
+ }
+ </style>
 動作
 動作 
「ときめき」と書かれたカードっぽいやつが表示されているかと思います 
 ソースコード
 ソースコード 
https://github.com/taiju59/lovely-vue/commit/19757cc150d98c5b86513c8dc16bad6f1517843e
さらに、タスクを追加できるようにしましょう。
テンプレート側に追加ボタンを記述します。
  <input v-model="message" />
+ <button @click="add">追加</button>
  <br />
スクリプト側に add という関数を定義し、この中で新しいタスクを追加します。
-   })
+   }),
+   methods: {
+     add() {
+       const newTodo = {
+         id: this.todos.length + 1,
+         text: this.message
+       };
+       this.todos.push(newTodo);
+       this.message = "";
+     }
+   }
  };
 動作
 動作 
「追加」ボタンを押すとタスクが追加さるようになっていれば OK です 
 ソースコード
 ソースコード 
https://github.com/taiju59/lovely-vue/commit/838eaf5f88deee4b886c26618793c83c020cd2a4
 ポイント
 ポイント 
- 
v-forでテンプレート内で for 文が回せる- 
key属性にはループ内で一意となる値を設定しましょう(スタイルガイド参照)
 
- 
5. 算出プロパティ( computed )
気付いちゃった人もいるかもしれませんが、
このままでは入力欄が空でもタスクの追加ができちゃいます。
ダサいので、入力欄が空の場合は追加ボタンを無効化しましょう。
スクリプト部分に computed とその中に isDisabled という関数を定義します。
    message: "Spark joy!"
  }),
+ computed: {
+   isDisabled() {
+     return this.message.length === 0;
+   }
+ },
  methods: {
message が空なら true を返します。
これをテンプレート部分で使います。
- <button @click="add">追加</button>
+ <button :disabled="isDisabled" @click="add">追加</button>
これで、
入力内容が空の時は isDisabled が true => disabled が有効化、
入力内容がある時は isDisabled が false => disabled が無効化
します。
 動作
 動作 
入力欄が空の時に「追加」ボタンが押せなくなっていれば OK です 
 ソースコード
 ソースコード 
https://github.com/taiju59/lovely-vue/commit/3778dd29f1d34284eb68861ae973edcbf58c9d28
 ポイント
 ポイント 
- 
computedで算出プロパティを宣言- 定数や、既存の各変数から自動的に導き出される値を取得するために使います
- 導出元の変数が変化すれば computedの結果も変わり、これに応じて DOM も変化します
 
- HTML 属性に値を指定する際、 :disabledなど:を前に付けると右辺はスクリプトとして変数が展開されます4
おわりに
Vue の基本についてかいつまんで学びました。
ここまでやれば、小さいものなら何か作れるちゃうかも?と
思ってもらえたかもしれません。
また、リアクティブなフレームワークが初めての人は、
双方向バインディングや算出プロパティで リアクティブの風 を感じてもらえたら嬉しいです。
次回はコンポーネントをファイル分割する方法について学びます。
次回  やさしめ Vue.js チュートリアル(3) ~ コンポーネントによる構成
 やさしめ Vue.js チュートリアル(3) ~ コンポーネントによる構成
Lovely Vue 
- 
https://jp.vuejs.org/v2/guide/components.html#data-%E3%81%AF%E9%96%A2%E6%95%B0%E3%81%A7%E3%81%AA%E3%81%91%E3%82%8C%E3%81%B0%E3%81%AA%E3%82%8A%E3%81%BE%E3%81%9B%E3%82%93 ↩ 
- 
本質的な解決方法としてはちょっとずれているかもしれませんが、今回はv-modelの紹介が目的のためv-modelを使います ↩ 
- 
これは v-bind:の省略記法です。 ↩
