前回の記事では子コンポーネントにpropsと$emitを実装し、親コンポーネントからv-modelディレクティブを使用して双方向バインディングを行いデータをやり取り(同期)する方法を紹介しました。
Vue.jsのバージョンが2.x以前の時は、コンポーネントのカスタム要素で指定するv-modelディレクティブは、以下のように1つしか記述できませんでした。
1 |
<custom-component v-model="データ"></custom-component> |
しかし、バージョン3.xからは、単一のコンポーネントの要素(インスタンス)に対して以下のように複数のv-modelディレクティブを使用できるようになりました。
1 2 3 4 |
<custom-component v-model="データ1" v-model="データ2" v-model="データ3"> </custom-component> |
そこで今回は、単一のコンポーネントインスタンスで、複数のv-modelディレクティブを使用する方法について紹介します。
目次
1つのコンポーネントにv-modelディレクティブで複数のデータをバインドする
1つのコンポーネントで複数のデータをv-modelディレクティブでバインドするのは、とても簡単です。
子コンポーネント側のpropsに複数のプロパティを定義して、親コンポーネント側のHTMLテンプレートで単一のコンポーネント要素にv-modelディレクティブでのバインドを複数記述するだけです。
子コンポーネントの実装
以下に子コンポーネントの実装例を示します。
ここでは、顧客(カスタマー)情報を編集するための簡単なコンポーネントの「顧客シート」を作成します。
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 35 36 37 |
<script type="text/x-template" id="customer-template"> <div> <label for="lastName">氏名: <input type="text" id="lastName" v-bind:value="lastName" v-on:input="$emit('update:lastName', $event.target.value)"> <input type="text" id="firstName" v-bind:value="firstName" v-on:input="$emit('update:firstName', $event.target.value)"> </label> </div> <div> <label for="gender">性別: <select id="gender" v-bind:value="gender" v-on:input="$emit('update:gender', $event.target.value)"> <option value="1">男性</option> <option value="2">女性</option> <option value="3">その他</option> </select> </label> </div> <div> <label for="birthday">生年月日: <input type="date" id="birthday" v-bind:value="birthday" v-on:input="$emit('update:birthday', $event.target.value)"> </label> </div> </script> |
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 |
const customerSheet = { template: '#customer-template', props: { firstName: { type: String, default: '', required: true }, lastName: { type: String, default: '', required: true }, gender: { type: String, default: '', required: true }, birthday: { type: String, default: '', required: true }, } } |
子コンポーネントのHTMLテンプレートには、氏名(姓・名)と性別、生年月日をフィールドとして配置します。
そして、配置したフィールドにデータをバインドするためのプロパティをpropsにそれぞれ定義します。ここでは氏名(名)をfirstName、氏名(姓)をlastName、性別をgender、生年月日をbirthdayというプロパティで定義しています。
propsに定義したプロパティはそれぞれのフィールドにv-bindディレクティブでバインドし、v-onディレクティブでinputイベントに$emitメソッドを指定しています。
親コンポーネントの実装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<div id="app"> <customer-sheet v-model:last-name="lastName" v-model:first-name="firstName" v-model:gender="gender" v-model:birthday="birthday"> </customer-sheet> <hr> <div> 氏名(姓): {{lastName}} </div> <div> 氏名(名): {{firstName}} </div> <div> 性別: {{gender}} </div> <div> 生年月日: {{birthday}} </div> </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Vue.createApp({ components: { 'customer-sheet': customerSheet }, data: function() { return { firstName: '', lastName: '', gender: '', birthday: '' } } }).mount('#app') |
親コンポーネント側では、顧客シートコンポーネントをHTMLテンプレートに定義し、コンポーネントのpropsに定義したプロパティをv-modelディレクティブでバインドします。
実行結果
上記の子コンポーネントと親コンポーネントのソースコードを実行した結果は次のようになります。
氏名、性別、生年月日に値を入力すると、バインドしているデータが表示されることが確認できます。
性別では、option要素に定義したvalueがバインドされ、生年月日では日付の「YYYY-MM-DD」形式のデータがバインドされています。
実行結果がすぐ確認できるようにjsFiddleのHTMLパネルに貼り付けて実行できるコードを記載しておきます。
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
<script src="https://unpkg.com/vue@next"></script> <script type="text/x-template" id="customer-template"> <div> <label for="lastName">氏名: <input type="text" id="lastName" v-bind:value="lastName" v-on:input="$emit('update:lastName', $event.target.value)"> <input type="text" id="firstName" v-bind:value="firstName" v-on:input="$emit('update:firstName', $event.target.value)"> </label> </div> <div> <label for="gender">性別: <select id="gender" v-bind:value="gender" v-on:input="$emit('update:gender', $event.target.value)"> <option value="1">男性</option> <option value="2">女性</option> <option value="3">その他</option> </select> </label> </div> <div> <label for="birthday">生年月日: <input type="date" id="birthday" v-bind:value="birthday" v-on:input="$emit('update:birthday', $event.target.value)"> </label> </div> </script> <div id="app"> <customer-sheet v-model:last-name="lastName" v-model:first-name="firstName" v-model:gender="gender" v-model:birthday="birthday"> </customer-sheet> <hr> <div> 氏名(姓): {{lastName}} </div> <div> 氏名(名): {{firstName}} </div> <div> 性別: {{gender}} </div> <div> 生年月日: {{birthday}} </div> </div> <script> const customerSheet = { template: '#customer-template', props: { firstName: { type: String, default: '', required: true }, lastName: { type: String, default: '', required: true }, gender: { type: String, default: '', required: true }, birthday: { type: String, default: '', required: true }, } } Vue.createApp({ components: { 'customer-sheet': customerSheet }, data: function() { return { lastName: '', firstName: '', gender: '', birthday: '' } } }).mount('#app') </script> |
jsFiddleの使い方については、以下の記事を参考にしてください。
親コンポーネントのデータをオブジェクトにまとめる
上記のコードで親コンポーネントのdataオプションに定義しているデータは、オブジェクトにまとめることもできます。
先に示した実装例のコードで、親コンポーネントのdataオプションに定義するプロパティを、オブジェクト形式にした場合の例を以下に示します。
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
<script src="https://unpkg.com/vue@next"></script> <script type="text/x-template" id="customer-template"> <div> <label for="lastName">氏名: <input type="text" id="lastName" v-bind:value="lastName" v-on:input="$emit('update:lastName', $event.target.value)"> <input type="text" id="firstName" v-bind:value="firstName" v-on:input="$emit('update:firstName', $event.target.value)"> </label> </div> <div> <label for="gender">性別: <select id="gender" v-bind:value="gender" v-on:input="$emit('update:gender', $event.target.value)"> <option value="1">男性</option> <option value="2">女性</option> <option value="3">その他</option> </select> </label> </div> <div> <label for="birthday">生年月日: <input type="date" id="birthday" v-bind:value="birthday" v-on:input="$emit('update:birthday', $event.target.value)"> </label> </div> </script> <div id="app"> <customer-sheet v-model:last-name="customer.lastName" v-model:first-name="customer.firstName" v-model:gender="customer.gender" v-model:birthday="customer.birthday"> </customer-sheet> <hr> <div> 氏名(姓): {{customer.lastName}} </div> <div> 氏名(名): {{customer.firstName}} </div> <div> 性別: {{customer.gender}} </div> <div> 生年月日: {{customer.birthday}} </div> </div> <script> const customerSheet = { template: '#customer-template', props: { firstName: { type: String, default: '', required: true }, lastName: { type: String, default: '', required: true }, gender: { type: String, default: '', required: true }, birthday: { type: String, default: '', required: true }, } } Vue.createApp({ components: { 'customer-sheet': customerSheet }, data: function() { return { customer: { firstName: '', lastName: '', gender: '', birthday: '' } } } }).mount('#app') </script> |
ここでは、dataオプションに定義していたfirstName、lastName、gender、birthdayをcustomerというオブジェクトにまとめています。(96行目から100行目)