【写真とコード付き】Vue.jsで書く「v-model」双方向データバインディングの方法【６選】

<br>

## はじめに
この記事は**Vue.jsの基本は抑えられている程**で、話を進めていきます。もしVue.jsを初めて触ると言う方は、こちらの記事を参照していただければと思います。

⬇️**【写真とコード付き】Vue.jsの構築から基本的な書き方まで1から解説【超初心者向け】**
https://qiita.com/yuki4839/items/62f40564e3f4c8dbfc51
<br>

さて今回は、主にフォームタグに活用する `v-model` ディレクティブについて、詳しく解説していきます。

早速いきましょう。
<br>
<br>

## 実行環境
使用ツール、デバイスはこちら

<ul>
  <li>Google chrome</li>
  <li>Mac OS Catalina</li>
  <li>Visual Studio Code</li>
</ul>
<br>

また今回使用するディレクトリ階層はこちら。

```html:ディレクトリ階層
─ root(任意のディレクトリ)
│
├─ index.html
│
├─ css
│   └ style.css
│
└─ js
    └ main.js
```
<br>

各ファイルの初期値はこちら。

```html:index.html
<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>

  <link rel="stylesheet" href="./css/style.css">
</head>

<body>

  <div id="web">
    <p>
      {{ context }}
    </p>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
  <script src="./js/main.js"></script>
</body>

</html>
```

```css:style.css
/* 出力結果を見やすくするためのスタイルです */
body {
  background-color: #add8e6;
}

#web {
  background-color: #fff;
  margin: 20px;
  padding: 20px;
  width: 300px;
}
```

```js:main.js
const web = new Vue({
  el: '#web',
  data: {
    context: `Hello Vue.js!`
  }
})
```
<br>

現時点での出力結果はこちら。

![スクリーンショット 2020-12-12 21.13.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/882660/fb3335b5-8937-d54c-f34d-0c96d64eaff6.png)
<br>
<br>

## v-model
まず `v-model` ディレクティブについて簡単に説明すると、inputタグなどで入力された文字を、リアルタイムで `Vue.js` 側に渡す事ができるディレクティブになります。
<br>

### text
`v-model` ディレクティブで最も基本的な処理が、inputタグのtextタイプでの入力した値を、 `Vue.js` 側に渡す処理になります。

文章で伝えるよりも、コードを見て理解してもらうのが早いと思いますので、まずは実際のコードをご覧ください。

```html:index.html
<!-- headタグ省略 -->
<body>

  <div id="web">
    <p>
      <input type="text" v-model="message">
    </p>

    <!-- 確認用に Vue.js の値を表示 -->
    <pre>{{ $data }}</pre>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
  <script src="./js/main.js"></script>

</body>
```

```js:main.js
var web = new Vue({
	el: '#web',
  data: {
  	message: 'Hello Vue.js!'
  }
})
```
<br>

ポイントは、タグの属性に `v-model` を使用して、属性値の部分に `Vue.js` で定義したキーを代入する部分です。

これによって、inputタグで入力した値が、リアルタイムで `Vue.js` の `message` に反映されるようになります。
<br>

では動作確認です。
最初の画面はこちら。

![スクリーンショット 2020-12-13 22.16.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/882660/49485b07-c496-3faa-a728-87b68505045d.png)

では、inputタグに「Hello World!」と入力してみます。
その結果がこちら。

![スクリーンショット 2020-12-13 22.17.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/882660/a4f41a90-cf86-487f-d412-a3ea711166b8.png)

このように、inputタグなどで書いた値が、リアルタイムで `Vue.js` 側に反映されることを、`双方向データバインディング` と言います。

そして、これを実現するディレクティブが、`v-model` ディレクティブと言うことになります。
<br>
<br>

### textarea
次にtextareaタグでの `双方向データバインディング` の書き方はこちら。

```html:index.html
<!-- headタグ省略 -->
<body>

  <div id="web">
    <textarea v-model="message">{{ message }}</textarea>

    <pre>{{ $data }}</pre>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
  <script src="./js/main.js"></script>

</body>
```

```js:main.js
var web = new Vue({
	el: '#web',
  data: {
  	message: 'Hello Vue.js!'
  }
})
```
<br>

ポイントは、textareaタグの場合はタグの中に、`マスタッシュ構文` で挿入する必要がある点です。(これが無ければ正常に動作する事ができません。)
<br>

では動作確認です。
最初の画面はこちら。

![スクリーンショット 2020-12-13 22.25.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/882660/5980c64c-9d1c-0d90-9955-8d5223490268.png)

では、textareaタグに「Hello World!」と入力してみます。
その結果がこちら。

![スクリーンショット 2020-12-13 22.25.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/882660/cab68862-837f-49db-df5d-aae42c0fee43.png)

因みに "\n" とあるのは改行になります。
<br>
<br>

### checkbox
次はチェックボックスでの `双方向データバインディング` についてです。まず実際のコードは以下の通りになります。

```html:index.html
<body>

  <div id="web">
	<input type="checkbox" id="checkbox" v-model="checked">
	<label for="checkbox">{{ checked }}</label>
	<pre>{{ $data }}</pre>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
  <script src="./js/main.js"></script>

</body>
```

```js:main.js
var web = new Vue({
  el: '#web',
  data: {
    checked: false
  }
})
```

ポイントは、checkboxタイプのinputタグに `v-model="checked"` を挿入している部分になります。これによって、チェックが入れられると、`Vue.js` のcheckedが `true` にリアルタイムで書き換えられます。(逆も同じ)
<br>

では動作確認です。
最初の画面はこちら。

![スクリーンショット 2020-12-13 23.28.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/882660/ca4c1626-1e41-9e17-69ac-d154693fde71.png)

ではチェックを入れます。

![スクリーンショット 2020-12-13 23.28.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/882660/5688ef02-30b6-e7ed-982d-5ebceef8ae24.png)

そしてチェックを外すと、先ほどの `false` と同じ状態になります。
<br>
<br>

### radio
次はラジオボタンでの `双方向データバインディング` についてです。まず実際のコードは以下の通りになります。

```html:index.html
<div id="web">
	<select v-model="selected">
		<option disabled value="">Please select one</option>
		<option>Red</option>
		<option>yellow</option>
		<option>Green</option>
	</select>
	<p>{{ selected }}</p>
</div>
```

```js:main.js
var web = new Vue({
  el: '#web',
  data: {
    selected: ''
  }
})
```
<br>

ポイントは、selectタグに `v-model` 属性を使用する事です。これにより、選択した値が `Vue.js` にリアルタイムで書き換えられます。
<br>

では動作確認です。
最初の画面はこちら。

![スクリーンショット 2020-12-13 22.56.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/882660/a54d29e7-52e6-b85e-7fea-d885f8745a8a.png)

では選択をします。
まずはRedです。

![スクリーンショット 2020-12-13 22.56.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/882660/554f9b37-e506-1143-dc5b-388442e842fe.png)

続いてyellowです。

![スクリーンショット 2020-12-13 22.56.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/882660/3499d0f9-567b-24c9-8065-94f6f4f4edda.png)

最後にGreenです。

![スクリーンショット 2020-12-13 22.57.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/882660/dfbe4650-bbf9-1e85-c112-bd11d34aac3b.png)

このように選択した値が、代入される仕組みを作る事ができます。
<br>
<br>

### option
最後に `v-model` に関するオプションをいくつか解説していきます。
<br>

#### lazy
こちらは `双方向データバインディング` を行うタイミングを、遅延させる事ができるオプションです。

実際のコードはこちら。

```html:index.html
<!-- headタグ省略 -->
<body>

  <div id="web">
    <input type="text" v-model.lazy="message">
    <p>{{ message }}</p>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
  <script src="./js/main.js"></script>

</body>
```

```js:main.js
var web = new Vue({
  el: '#web',
  data: {
    message: ''
  }
})
```
<br>

ポイントは、`v-model` に `.lazy` と追記する事です。これにより、入力した値からカーソルが離れるまで、`双方向データバインディング` がされないようにする事ができます。
<br>

では動作確認です。
最初の画面はこちら。

![スクリーンショット 2020-12-13 23.03.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/882660/48b006c8-676d-fd27-fc95-d533a8aec008.png)

次に「Hello Vue.js!」と入力。

![スクリーンショット 2020-12-13 23.03.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/882660/9430393b-b1f7-4f34-c754-f9bcaaf11159.png)

ご覧のように、カーソルが離れない限りは、`双方向データバインディング` がされません。

カーソルを離した結果がこちら。

![スクリーンショット 2020-12-13 23.05.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/882660/705cb562-5ddf-7b69-17e9-c878c7b62687.png)

このように、値の確定を遅延させる事ができます。
<br>
<br>

#### trim
こちらは `双方向データバインディング` を行う際に、空白スペースを除去する事ができるオプションです。(半角全角どちらも)

実際のコードはこちら。

```html:index.html
<!-- headタグ省略 -->
<body>

  <div id="web">
    <input type="text" v-model.trim="message">
    <p>{{ message }}</p>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
  <script src="./js/main.js"></script>

</body>
```

```js:main.js
var web = new Vue({
  el: '#web',
  data: {
    message: ''
  }
})
```
<br>

ポイントは先程と同じく、`v-model` に `.trim` と追記する事です。これにより、入力した値の前後の空白を、`双方向データバインディング` がされる際に除去できます。
<br>

では動作確認です。
最初の画面はこちら。

![スクリーンショット 2020-12-13 23.09.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/882660/2f7e0681-f7bb-32f8-38c0-d7d64e7036ba.png)

次に前後に空白を付けて、「Hello Vue.js!」と入力。

![スクリーンショット 2020-12-13 23.10.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/882660/8cfe317e-4681-e80b-3771-e19435017441.png)

このように、空白を除去する事ができます。
<br>
<br>

## まとめ
以上が `v-model` ディレクティブによる、`双方向データバインディング` の記述方法になります。もう一度まとめておくと、以下の通りになります。

<ul>
  <li>text
    <ul>
      <li>input type="text" v-model="message"</li>
    </ul>
  </li>
  <li>textarea
    <ul>
      <li>textarea v-model="message"<br>(タグ内にマスタッシュ構文)</li>
    </ul>
  </li>
  <li>checkbox
    <ul>
      <li>input type="checkbox" id="checkbox" v-model="checked"</li>
    </ul>
  </li>
  <li>radio
    <ul>
      <li>select v-model="selected"</li>
    </ul>
  </li>
  <li>option
    <ul>
      <li>lazy
        <ul>
          <li>input type="text" v-model.lazy="message"</li>
        </ul>
      </li>
      <li>trim
        <ul>
          <li>input type="text" v-model.trim="message"</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

ぜひ実際にコードを書いて見てください！
最後まで読んでいただき、ありがとうございました！
<br>
<br>
<br>

筆者：yuki｜IT業界のリアルな転職事情など発信｜元トップ営業マン(訪販)→未経験からエンジニア転職へ
Qiita：https://qiita.com/yuki4839
Twitter：https://twitter.com/yukifullstack
<br>