前回は子コンポーネントに実装したpropsと$emitを利用して、親コンポーネントのデータをやり取り(同期)する方法を紹介しました。
前回の記事では、子コンポーネントにpropsで定義したプロパティを親コンポーネント側ではv-bindディレクティブでバインドし 、$emitで発行したイベントを親コンポーネント側ではv-onディレクティブでバインドする方法でデータをやり取りしていましたが、Vue.jsには、親子コンポーネント間のデータのやり取り(同期)にv-modelディレクティブが利用できます。
そこで今回は、子コンポーネントに定義したpropsのプロパティと$emitのイベントを、親コンポーネントではv-modelディレクティブでバインドする方法について紹介します。
目次
v-modelディレクティブ
HTMLに用意されているinput要素にVue.jsでデータをバインディングする際は、v-modelディレクティブが使用できます。
v-modelディレクティブでinput要素にデータをバインドするには、以下のようなコードを記述します。
1 |
<input type="text" v-model="input要素にバインディングするデータ" /> |
このv-modelディレクティブを使用したバインドは、input要素のvalueプロパティをv-bindディレクティブでバインドし、inputイベントをv-onディレクティブでバインドする記述の糖衣構文(syntax sugar: シンタックス・シュガー)になります。
v-modelディレクティブで利用するための実装
親コンポーネント側でv-modelディレクティブを利用して、子コンポーネントとデータを双方向バインドする場合は、子コンポーネント側のpropsに「modelValue」という名前でプロパティを定義し、イベントを発行する$emitメソッドの第1引数に「update:modelValue」という値を指定します。
Vue.jsの既定の動作では「modelValue」propsのデータを$emitメソッドでupdateすることで、v-modelディレクティブを使用したデータのバインディングができるようになります。
子コンポーネントの実装例
子コンポーネント側の実装例を以下に示します。
1 2 3 4 5 6 |
<script type="text/x-template" id="custom-component"> <input type="number" v-bind:value="modelValue" v-on:input="onInput"/> </script> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
const inputNumber = { template: '#custom-component', props: { modelValue: { type: String, default: '', required: true } }, methods: { onInput: function(e) { this.$emit('update:modelValue', e.target.value) } } } |
上記の例では、子コンポーネント側のHTMLテンプレートに配置したinput要素のvalueにpropsに定義したmodelValueをv-bindディレクティブでバインドし、$emitメソッドでmodelValueをinput要素の値で更新するメソッド(onInput)をinput要素のinputイベントにv-onディレクティブでバインドしています。
親コンポーネントの実装例
親コンポーネント側の実装例を以下に示します。
1 2 3 4 5 6 |
<div id="app"> <input-number v-model="parentValue"></input-number> <div> {{parentValue}} </div> </div> |
1 2 3 4 5 6 7 8 9 10 |
Vue.createApp({ components: { 'input-number': inputNumber }, data: function() { return { parentValue: 0 } } }).mount('#app') |
上記の例では、子コンポーネント側に実装したpropsと$emitを、子やコンポーネント側でv-modelディレクティブを使用してバインドしています。
v-modelディレクティブでバインドすることにより、バインドするデータ(dataオプションに定義したparentValue)が子コンポーネントと双方向にバインドされ同期します。
propsにmodelValue以外のプロパティを使用する場合
ここまでのサンプルでは、親コンポーネントでv-modelディレクティブを使用して子コンポーネントとデータをバインドする場合は、子コンポーネント側のpropsに「modelValue」というプロパティを定義する方法を紹介しましたが、propsに定義するプロパティ名は「modelValue」以外にすることもできます。
子コンポーネント側のpropsに定義するプロパティ名を「modelValue」以外にする場合は、親コンポーネント側でv-modelディレクティブでバインドする際にpropsのプロパティ名を引数として与えます。
上記のv-modelでのバインド例をもとにpropsのプロパティ名を「numberValue」に変更した場合のコードを以下に示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<!-- 子コンポーネント --> <script type="text/x-template" id="custom-component"> <input type="number" v-bind:value="numberValue" v-on:input="onInput"/> </script> <!-- 親コンポーネント --> <div id="app"> <input-number v-model:number-value="parentValue"></input-number> <div> {{parentValue}} </div> </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
// 子コンポーネント const inputNumber = { template: '#custom-component', props: { numberValue: { type: String, default: '', required: true } }, methods: { onInput: function(e) { this.$emit('update:numberValue', e.target.value) } } } // 親コンポーネント Vue.createApp({ components: { 'input-number': inputNumber }, data: function() { return { parentValue: 0 } } }).mount('#app') |
上記の例では、子コンポーネントでpropsに定義するプロパティ名を「numberValue」にしていますので、親コンポーネント側のv-modelディレクティブの記述に引数として「numberValue」を与えて「v-model:numberValue」の形式でデータをバインドしています。(HTMLの11行目)
【補足】Vue.js バージョン2.xでのv-modelの実装
ここでは参考のために、Vue.jsのバージョンが2.xの時の動作についても記載しておきます。
Vue.jsのバージョンが2.xの時は、v-modelはpropsにvalueプロパティを定義し$emitにinputイベントを定義することで利用ができました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<!-- コンポーネント(子) --> <script type="text/x-template" id="custom-component"> <input type="number" v-bind:value="counter" v-on:input="onInput"/> </script> <!-- アプリケーション(親) --> <div id="app"> <input-number v-model="value"></input-number> <div> {{value}} </div> </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
// コンポーネント(子) const inputNumber = { template: '#custom-component', props: { value: { type: String, default: '', required: true } }, data: function() { return { counter: 0 } }, methods: { onInput: function() { this.counter++; this.$emit('input', this.counter); } } } // アプリケーション(親) new Vue({ components: { 'input-number': inputNumber }, data: function() { return { value: 0 } } }).$mount('#app') |
Vue.js 3.xではこのpropsに「value」を定義して$emitで「input」イベントを発行している部分が、propsに「modelValue」を定義して$emitで「update:modelValue」イベントを発行する処理に替わります。