Vue.js: 子コンポーネントのチェックボックスを双方向にデータバインディングする

Vue.jsでチェックボックスを双方向にデータバインディングする例としては、多くの場合[`v-model`ディレクティブ](https://jp.vuejs.org/v2/api/#v-model)が用いられます。けれど、[単一ファイルコンポーネント](https://jp.vuejs.org/v2/guide/single-file-components.html#ad)を使って、子コンポーネントのチェックボックスをバインディングしようとすると、`v-model`だけで手軽にはできません。この場合のやり方について、ご説明します。


# ひな形のVueプロジェクトをつくる
親子の単一ファイルコンポーネントを使ったプロジェクトは、Vue CLIでひな形が簡単にできます。このひな形からサンプルをつくることにしましょう。つぎのように、`vue create`コマンドにプロジェクト名(`binding_checkbox`)を添えて実行します。詳しくは、「[Vue CLI 3入門 04: プロジェクトをつくる](http://fumiononaka.com/Business/html5/FN1807004.html)」をお読みください

```terminal
$ vue create binding_checkbox
```

プロジェクトのディレクトリに移ったうえで、`npm run serve`と打ち込めば、アプリケーションがローカルサーバーで開きます。URLは[http://localhost:8080/](http://localhost:8080/)です。


# 親と子のコンポーネントにチェックボックスを加える
ひな形は、アプリケーション(`vue:src/App.vue`)に子コンポーネント(`src/components/HelloWorld.vue`)がひとつ加えられています。それぞれのコンポーネントファイル(VUE)の中身を以下のように書き替えて、親と子にひとつずすつチェックボックスとラベル、およびボタンの一式を加えましょう(図001)。

#### 図001■親と子のコンポーネントにチェックボックス/ラベル/ボタンが加えられた
![1812005_001.png](https://qiita-image-store.s3.amazonaws.com/0/128949/0b103e4a-426f-a857-2362-899d173f3f49.png)

```vue:src/App.vue
<template>
  <div id="app">
    <input type="checkbox" id="parent">
	<label for="parent">parent</label>
	<button>toggle</button>
    <HelloWorld :check_child="check_child"/>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'app',
  components: {
    HelloWorld
  },
  data() {
	  return {
		  check_parent: false,
		  check_child: false
	  };
  }
}
</script>
```

```vue:src/components/HelloWorld.vue
<template>
  <div class="hello">
    <input type="checkbox" id="child">
	<label for="child">child</label>
	<button>toggle</button>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: {
    check_child: Boolean
  }
}
</script>
```

データバインディングは、簡単な準備だけ済ませました。まずは、アプリケーション(`src/App.vue`)です。`<script>`のVueインスタンスには`data`オプションに、チェックボックスの状態をもたせる予定のプロパティが、親(`check_parent`)と子(`check_child`)のためにひとつずつ用意してあります。そして、子のプロパティは、`<template>`にディレクティブ[`v-bind`](https://jp.vuejs.org/v2/api/#v-bind)([省略記法`:`](https://jp.vuejs.org/v2/guide/syntax.html#v-bind-省略記法))でバインドしなければなりません。

つぎに、子コンポーネント(`src/components/HelloWorld.vue`)では、`<script>`のVueインスタンスに`props`オプションで親からバインドされたプロパティ(`check_child`)を受け取ります。これで、子コンポーネントは親からデータバインディングされた値が使えるようになったわけです。

# 親アプリケーションのチェックボックスを双方向データバインディングする
親アプリケーション(`src/App.vue`)の中の双方向データバインディングは、ひとつのモジュール内ですので`v-model`ディレクティブが使えます。したがって、ディレクティブをつぎのようにチェックボックス(`<input>`)に加えるだけです。また、ボタンは[`v-on:click`](https://jp.vuejs.org/v2/api/#v-on)([省略記法`@`](https://jp.vuejs.org/v2/guide/syntax.html#v-on-省略記法))イベントにリスナーメソッド(`toggleParentCheck()`)を定め、バインドされたプロパティ(`check_parent`)のブール値が反転できるようにしました。

```vue:src/App.vue
<template>
  <div id="app">
    <input type="checkbox" id="parent" v-model="check_parent">

	<button @click="toggleParentCheck">toggle</button>

  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {

  methods: {
	  toggleParentCheck() {
		  this.check_parent = !this.check_parent;
	  }
  }
}
</script>
```

これで、双方向のデータバインディングができました。チェックボックスの操作だけでなく、ボタンでもチェックのオン/オフが切り替えられます。チェックボックスの状態がデータ(`check_parent`)とバインドされているからです。


# 子コンポーネントのチェックボックスを双方向データバインディングする
単一ファイルコンポーネントで別モジュールにした場合、データバインディングは親から子へと子から親へとを、それぞれ定めなければなりません(「[コンポーネントで v-model を使う](https://jp.vuejs.org/v2/guide/components.html#コンポーネントで-v-model-を使う)」参照)。

前掲ふたつのコンポーネントのコードでは、親(src/App.vue)から子(src/components/HelloWorld.vue)にバインドするデータ(`check_child`)は渡されていました。それを`<template>`で以下のように`v-bind:value`(省略記法`:value`)に与えれば、要素が値を取り出せます。値は確かめられるように、`{{}}`でテキストとして示しました。

子から親へは、イベントで送ります。`<input>`要素を操作するイベントは`v-on:input`(省略記法`@input`)です。リスナーのメソッド(`toggleChildCheck()`)は、[`$emit()`メソッド](https://jp.vuejs.org/v2/api/#vm-emit)で親にイベント(`toggleChildCheck`)と引数の値を送ります。

```vue:src/components/HelloWorld.vue
<template>
  <div class="hello">
    <input type="checkbox" id="child"
		:value="check_child"
		@input="toggleChildCheck">

	<button @click="toggleChildCheck">toggle</button>
	{{check_child}}
  </div>
</template>

<script>
export default {

  methods: {
	  toggleChildCheck() {
		  this.$emit('toggleChildCheck', !this.check_child);
	  }
  }
}
</script>
```

親のアプリケーション(`src/App.vue`)は、`v-on`(`@`)で受け取ったイベント(`toggleChildCheck`)から、リスナーメソッド(`toggleChildCheck()`)を呼び出し、引数(`value`)の値でバインドしているプロパティ(`check_child`)を改めます。これで、子から親へのデータバインディングもでき上がりです。

```vue:src/App.vue
<template>
  <div id="app">

    <HelloWorld

		@toggleChildCheck="toggleChildCheck"/>
  </div>
</template>

<script>

export default {

  methods: {

	  toggleChildCheck(value) {
		  this.check_child = value;
		  console.log(this.check_child);
	  }
  }
}
</script>
```

ただし、ひとつ問題があります。ボタンでプロパティのブール値は変わるものの、チェックボックスのチェック表示が切り替わりません(図002)。

#### 図002■ボタンで変わるのはプロパティ値だけでチェック表示はそのまま
![1812005_002.png](https://qiita-image-store.s3.amazonaws.com/0/128949/9d5cd878-85ec-9781-10d8-eb615037e450.png)


# checked属性をバインディングする
チェックボックスの`<input>`要素(`type`属性`checkbox`)でチェックのあるなしは`checked`属性が決めます。したがって、この属性もバインディングしなければならないのです(とくに`value`属性値を使わない場合は、この属性へのバインディングはなくしても構いません)。

```vue:src/components/HelloWorld.vue
<template>
  <div class="hello">
    <input type="checkbox" id="child"
		:checked="check_child"

		>

  </div>
</template>
```

これで、親アプリケーションと同じように、、子コンポーネントのチェックボックスはボタンでも操作できるようになります。


# v-modelをカスタマイズして子コンポーネントに使う
`v-model`ディレクティブはカスタマイズすれば、別モジュールの子コンポーネントと双方向にデータバインディングできます。デフォルトの`v-model`が値を受け取るプロパティは`value`で、送るイベントは`input`です。Vueインスタンスに`model`オプションを定めれば、これらが変えられます(「[v-model を使ったコンポーネントのカスタマイズ](https://jp.vuejs.org/v2/guide/components-custom-events.html#v-model-を使ったコンポーネントのカスタマイズ)」参照)。

今回は、バインドするプロパティを`checked`にすれば解決です。ただ、チェックボックスの操作は`input`だけでなく、`change`イベントでも扱えます(「[\<input type="checkbox">](https://developer.mozilla.org/ja/docs/Web/HTML/Element/Input/checkbox)」参照)。そこで、イベントも`change`に変えて確かめてみましょう。書き替える子コンポーネント(`src/components/HelloWorld.vue`)のコードはつぎのとおりです。

```vue:src/components/HelloWorld.vue
<template>
  <div class="hello">
    <input type="checkbox" id="child"
		:checked="check_child"
		@change="toggleChildCheck">
		<!-- :value="check_child"
		@input="toggleChildCheck" -->

  </div>
</template>

<script>
export default {

  model: {
    prop: 'check_child',
    event: 'change'
  },

}
</script>
```

親アプリケーション(`src/App.vue`)は、`<template>`で子コンポーネントに与えていたプロパティの`v-bind`(`:`)をつぎのように`v-model`に差し替えます。子からのイベント(`toggleChildCheck`)の受け取りは、そのまま使うのでなくせません。

```vue:src/App.vue
<template>
  <div id="app">

    <!-- HelloWorld :check_child="check_child" -->
    <HelloWorld v-model="check_child"
		@toggleChildCheck="toggleChildCheck"/>
  </div>
</template>
```

これでも、子コンポーネントのチェックボックスが双方向にデータバインディングできます。もっとも、前項のコード例より、とくに短くもわかりやすくもなっていません。明示的に`v-model`ディレクティブを使う必要がある場合でなければ、前項のやり方で差し支えないでしょう。