【Vue.js】v-model解体新書

# 【Vue.js】v-model解体新書

## 前書き

### こんな人向け
- `v-model`は脳死で使っている。
- 独自コンポーネントで使おうとしたらなんかエラーが出た。
- 数値入力は`v-model.number`で完璧だと信じている。
- `v-model`完全に理解した。

上記は全て著者のことです。「半端な覚悟で`v-model`を語るんじゃねぇぞ！」と思った方は回れ左。（どっち回りでも変わらんよな）
半年くらい`Vue`に触れてきましたが、ずっとなんとなくで使っていました。ですが、最近ようやく重い腰をあげたので、ここにまとめておこうと思います。

## 本編

### 1. そもそもv-modelって何者？
早速、公式先生から引用
> form の input 要素 や textarea 要素、 select 要素に双方向 (two-way) データバインディングを作成するには、v-model ディレクティブを使用することができます。

うん？わかったようなわからないような・・・。まぁ、噛み砕いて説明を。

そもそも、`v-model`の役割は「変更とデータを紐づけること」です。それを実現するには、①表示するデータ、②変更があればデータに反映する、という2つが必要ですね。その2つをセットにしたのが`双方向 (two-way) データバインディング`というわけです。

使い方はこんな感じ。

```example.vue
<template>
  <input v-model="username" />
  <p>username: {{ username }}</p>
</template>

<script>
// 初期値として username が input に渡される
// input の変更が username に反映される
export default {
  data: () => {
    return {
      username: "username"
    };
  }
}
</script>
```

![q_1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252036/0acfb6d2-09a8-cfde-a2b0-3c9abffbd33b.gif)

ご覧の通り、入力欄とデータが双方向にリンクして連動しているのが分かるかと思います。
「これだけ？」と思うかもしれませんが、「これだけ」でございます、ハイ。

・・・流石に「これだけ」ではタイトル詐欺ですのでもう少し深掘りしていきます。

### 2. v-modelを解剖しよう
またまた公式から引用

> v-model はユーザーの入力イベントにおいてデータを更新するための基本的な糖衣構文 (syntax sugar) で、それに加えて、いくつかのエッジケースに対しては特別な配慮をしてくれます。

`v-model` は糖衣構文、つまりは何かの省略記法、`v-model` という機能では無くて、複数の機能の組み合わせでできてますよ、とのこと。

1章の「①表示するデータ、②変更があればデータに反映するの組み合わせ」は要素ごとに以下のように決められています。（公式から引用）

> - テキストと複数行テキストは、value プロパティと input イベントを使用します
- チェックボックスとラジオボタンは、checked プロパティと change イベントを使用します
- 選択フィールドは、value プロパティと change イベントを使用します

1章の例は一つ目の「テキスト」ですので、`value`と`input`に展開することができます。

```example_value-input.vue
<template>
  <input v-model="username" />
  <input :value="username" @input="username = $event.target.value" />
  <p>username: {{ username }}</p>
</template>

<script>
// v-model と value-input が全く同じ挙動をする
export default {
  data: () => {
    return {
      username: "username"
    };
  }
}
</script>
```

![Kapture 2020-10-30 at 13.03.19.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252036/934131f8-a9c6-7e3c-92b2-0cfcbfdaf95e.gif)

### 3. コンポーネントでの使い方
恐らく、この記事にたどり着いた方は自作コンポーネントで `v-model` をどう扱えばいいのかを調べていたのではないでしょうか？~~というか、そうじゃないと `v-model` を深掘りしようとは思わないですよね。~~

自作コンポーネント内で `v-model` を使おうとして「 `props` で渡してその値を `v-model` に代入してしまえばいいだけでは？ :thinking: 」と思っていて、`Vue`先生に怒られた経験があるのではないでしょうか？
これは、 Vueコンポーネントの `props` が一方通行であるためですが、
2章の内容をうまく使ってあげるとこの問題が解決できます。

```Child.vue
<template>
  <input 
    :value="value"
    @input="$emit('input', $event.target.value)"
  />  
</template>

<script>
export default {
  props: ['value']
}
</script>
```

```Parents.vue
<template>
  <div>
    <child v-model="username"></child>
  </div>
</template>

<script>
import Child from "@/components/child";

export default {
  components: {
    Child
  },
  data() {
    return {
      username: "username"
    }
  }
}
</script>
```

解説すると

1. 親コンポーネントから子コンポーネントに対しての `v-model` は `:value` と `@input` に解釈される
2. 子コンポーネントの `<input>` に `value` （username） が渡る
3. 子コンポーネントの `<input>` で入力があれば `emit` で親の `v-model` に `$event.target.value` が渡ることでusernameに値が反映される

という流れですね。 
`props` 自体を変更せず、変更されることを `emit` で親に知らせてあげればいいわけです。

## あとがき
- 分かればなんてことないですが、そこまで結構詰まりました・・・。もっと実装例があれば、スッと理解できたのかもなぁとも思いました。~~（自分でやれ）~~