Vue.jsのフォーム入力バインディング

## 入力フィールド

#### テキストボックス
```html
<input type="text" v-model="message">
<p>{{ message }}</p>
```

```js
data() {
  return {
    message: 'test',
  }
}
```

#### テキストエリア
```html
<textarea v-model="message"></textarea>
<p>{{ message }}</p>
```

```js
data() {
  return {
    message: 'test\ntest',
  }
}
```

#### 単体チェックボックス
```html
<input type="checkbox" id="test-cb" v-model="checked">
<label for="test-cb">{{ checked ? "checked" : "unchecked" }}</label>
```

```js
data() {
  return {
    checked: false,
  }
}
```

#### 複数チェックボックス
チェックすると`languages`に対象のチェックボックスの`value`の値が格納され、
チェックを外すと`languages`から対象のチェックボックスの`value`の値が削除される。

```html
<input type="checkbox" id="javascript" value="JavaScript" v-model="languages">
<label for="javascript">JavaScript</label>
<input type="checkbox" id="golang" value="Go" v-model="languages">
<label for="golang">Go</label>
<input type="checkbox" id="ruby" value="Ruby" v-model="languages">
<label for="ruby">Ruby</label>
```

```js
data() {
  return {
    languages: [],
  }
}
```

#### ラジオボタン
```html
<input type="radio" id="javascript" value="JavaScript" v-model="language">
<label for="javascript">JavaScript</label>
<input type="radio" id="golang" value="Go" v-model="language">
<label for="golang">Go</label>
<input type="radio" id="ruby" value="Ruby" v-model="language">
<label for="ruby">Ruby</label>
```

```js
data() {
  return {
    language: '',
  }
}
```

#### 単体セレクトボックス
```html
<select v-model="language">
  <option disabled value="">Select language</option>
  <option value="javascript">JavaScript</option>
  <option value="golang">Go</option>
  <option value="ruby">Ruby</option>
</select>
```

```js
data() {
  return {
    language: '',
  }
}
```

#### 複数セレクトボックス
選択すると`languages`に対象の選択肢の`value`の値が格納され、
選択を外すと`languages`から対象の選択肢の`value`の値が削除される。

```html
<select v-model="languages" multiple>
  <option disabled value="">Select languages</option>
  <option value="javascript">JavaScript</option>
  <option value="golang">Go</option>
  <option value="ruby">Ruby</option>
</select>
```

```js
data() {
  return {
    languages: [],
  }
}
```

## 修飾子

#### .lazy
データの同期を遅延する。
入力時には同期されず、enterキーやtabキー、他の要素をクリックするなどすると、データが同期される。

```html
<input type="text" v-model.lazy="message">
<p>{{ message }}</p>
```

```js
data() {
  return {
    message: 'test',
  }
}
```

#### .number
入力値を`Number`として自動的に型変換してからデータを同期する。
`input`の`type`に`number`を指定しても、常に文字列が返されるため、入力値をそのまま計算に使いたい時などに有用。
内部的には`parseFloat()`を使っていて、変換できない場合は元の入力値が返される。

```html
<input type="number" v-model.number="height">
<p>{{ height + 10 }}</p>
```

```js
data() {
  return {
    height: 170,
  }
}
```

#### .trim
入力値の前後の空白を除去してからデータを同期する。
全角でも半角でも除去される。

```html
<input type="text" v-model.trim="message">
<p>{{ message }}</p>
```

```js
data() {
  return {
    message: 'test',
  }
}
```

## カスタム入力
`v-model`はコンポーネントに対しても利用することができる。

```html
<custom-input v-model="message"></custom-input>
```

`custom-input`のコードを書く前に、以下の２つのコードが同等であるということを理解する。

```html
<input v-model="message">
```

```html
<input :value="message" @input="message = $event.target.value">
```

データの単方向バインド（`:value="message"`）とイベントをハンドルする処理（`@input="message = $event.target.value"`）の組み合わせを、
双方向バインディング（`v-model`）が行っているということがわかる。

`v-model`をコンポーネントに対して利用する場合、Vueは下記が同等となるように動作する。

```html
<custom-input v-model="message"></custom-input>
```

```html
<custom-input :value="message" @input="message = $event"></custom-input>
```

ここで大事になるのが、以下の2点である。

- `value`というpropsが`custom-input`に渡されている
- `input`イベントのハンドラは、`$event.target.value`ではなく`$event`をバインドされているデータ（`message`）に代入している

すると、通常のフォーム入力バインディングと同じように動作するためには、下記のように`custom-input`を実装する必要があるとわかる。

- propsの`value`を`input`要素の`value`にバインドする（単方向バインディング）
- `input`イベントのハンドラで、`input`イベントを入力値と共に親コンポーネントに通知する

```js
var CustomInput = {
  template: `<input :value="value" @input="$emit('input', $event.target.value)">`,
  props: {
    value: String
  }
}
```

上記のように実装すると、本項の初めに書いたように

```html
<custom-input v-model="message"></custom-input>
```

```js
components: {
  'custom-input': CustomInput
},
data() {
  return {
    message: 'test',
  }
}
```


と利用することができる。