LoginSignup
43
38

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-11-06

【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

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

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

2. v-modelを解剖しよう

またまた公式から引用

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

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

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

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

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

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

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 で親に知らせてあげればいいわけです。

あとがき

  • 分かればなんてことないですが、そこまで結構詰まりました・・・。もっと実装例があれば、スッと理解できたのかもなぁとも思いました。(自分でやれ)
43
38
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
43
38