質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Vue.js

Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

1回答

3639閲覧

v-forで作成した複数radio-buttonコンポーネントのv-modelでの管理方法

Apple_7777

総合スコア9

Vue.js

Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2020/05/06 06:57

前提・実現したいこと

Vue.jsで同一name属性を持つ複数ラジオボタンをv-modelで監視したいです

該当のソースコード

parent.vue

1 <div> 2 <radio-button 3 v-for="period in periodList" 4 :key="period.name" 5 :content="period" 6 :name="radioButtonName" 7 @change="$listeners['change']" 8 ></radio-button> 9 <div class="column column--12"></div> 10 </div> 11 12 13 <script> 14 export default { 15 props: { 16 periodList: { 17 type: Array, 18 default: () => [], 19 }, 20 checkedPeriodId: { 21 type: Number, 22 default: 0, 23 }, 24 }, 25 } 26 </script>

radio.vue

1<template> 2 <div class="hoge"> 3 <div class="fuga"> 4 <input 5 v-model="checked" 6 :id="`radio-${name}${content.id}`" 7 :value="content.id" 8 :name="name" 9 type="radio"> 10 <label 11 :for="`radio-${name}${content.id}`" 12 >{{ content.name }}</label> 13 </div> 14 </div> 15</template> 16 17<script> 18 19export default { 20 data: { 21 checked: 0 22 }, 23 props: { 24 content: { 25 type: Object, 26 require: true, 27 }, 28 name: { 29 type: String, 30 require: true, 31 }, 32 }, 33 watch: { 34 checked(val) { 35 this.$emit('change', val) 36 } 37 } 38} 39</script>

試したこと

上記記述のv-modelは正しくありません。
なぜなら、各radio-buttonコンポーネントで管理しているcheckedプロパティが異なるからです。
なので、v-modelを分解し、v-onでchangeイベント・v-bindでhtmlのcheckedを監視するようにしました。
すると正常に動作しました。
ですが、v-modelを使用して上記を実現できないものかと思い質問させて頂きました。

v-modelを分解し、v-onでchangeイベント・v-bindでhtmlのcheckedを監視するようにしました

上記のコードは以下です。

parent.vue

1 <div> 2 <radio-button 3 v-for="period in periodList" 4 :key="period.name" 5 :content="period" 6 :name="radioButtonName" 7 :is-checked="isChecked(period.id)" 8 @change="$listeners['change']" 9 ></radio-button> 10 <div class="column column--12"></div> 11 </div> 12 13 <script> 14 export default { 15 computed: { 16 isChecked() { 17 return id => id === this.checkedPeriodId 18 }, 19 }, 20 props: { 21 periodList: { 22 type: Array, 23 default: () => [], 24 }, 25 checkedPeriodId: { 26 type: Number, 27 default: 0, 28 }, 29 }, 30 } 31 </script>

radio.vue

1<template> 2 <div class="hoge"> 3 <div class="fuga"> 4 <input 5 :checked="isChecked" 6 :id="`radio-${name}${content.id}`" 7 :value="content.id" 8 :name="name" 9 @change="emit" 10 type="radio"> 11 <label 12 :for="`radio-${name}${content.id}`" 13 >{{ content.name }}</label> 14 </div> 15 </div> 16</template> 17 18<script> 19 20export default { 21 props: { 22 content: { 23 type: Object, 24 require: true, 25 }, 26 name: { 27 type: String, 28 require: true, 29 }, 30 isChecked: { 31 type: Boolean, 32 require: true, 33 }, 34 }, 35 methods: { 36 emit() { 37 this.$emit('change', this.content.id) 38 }, 39 }, 40} 41</script

補足情報(FW/ツールのバージョンなど)

Vue:2.5.17

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

参考にしたのはvuejs radio button componentです。

子コンポーネント

参考にしたstackoverflowのようにcomputedメソッドを使うことでv-modelで親コンポーネントのデータと同期することはできます。

RadioButton.vue

vue

1<template> 2 <label 3 :for="`${name}-${content.id}`" 4 > 5 <input 6 type="radio" 7 :id="`${name}-${content.id}`" 8 :name="name" 9 :value="content.value" 10 v-model="proxyValue" 11 > 12 <span>{{ content.name }}</span> 13 </label> 14</template> 15 16<script> 17export default { 18 name: "RadioButton", 19 props: { 20 content: { 21 type: Object, 22 require: true, 23 }, 24 name: { 25 type: String, 26 require: true, 27 }, 28 checkedValue: { 29 type: String, 30 require: true 31 } 32 }, 33 computed: { 34 proxyValue: { 35 get() { 36 return this.checkedValue 37 }, 38 set(newValue) { 39 this.$emit("change", newValue) 40 } 41 } 42 } 43} 44</script>

ただし、v-modelを使わずに下記のように書くこともでき、この方がコンパクトになる気がします。

vue

1<input 2 type="radio" 3 :id="`${name}-${content.id}`" 4 :name="name" 5 :value="content.value" 6 :checked="content.value === checkedValue" 7 @change="$emit('change', content.value)" 8>

ご存知とは思いますがradioのv-modelv-bind:checkedv-on:changeのシンタックスシュガーなので、この2つの書き方は意味的には同一と思っています。

親コンポーネント

比較のため、radioボタンを子コンポーネント化したパターンと非コンポーネントのパターンの両方のコードを載せています。

Parent.vue

vue

1<template> 2 <div> 3 <div> 4 <h2>component</h2> 5 <radio-button 6 v-for="period in periodList" 7 :key="`${radioButtonName1}-${period.id}`" 8 :content="period" 9 :name="radioButtonName1" 10 :checkedValue="checked1" 11 @change="updateValue1" 12 ></radio-button> 13 <div class="data">value = {{ checked1 }}</div> 14 </div> 15 <div> 16 <h2>non component</h2> 17 <label 18 v-for="period in periodList" 19 :key="`${radioButtonName2}-${period.id}`" 20 :for="`${radioButtonName2}-${period.id}`" 21 > 22 <input type="radio" 23 :id="`${radioButtonName2}-${period.id}`" 24 :name="radioButtonName2" 25 :value="period.value" 26 v-model="checked2" 27 > 28 <span>{{ period.name }}</span> 29 </label> 30 <div class="data">value = {{ checked2 }}</div> 31 </div> 32 </div> 33</template> 34 35<script> 36import RadioButton from '../components/RadioButton.vue' 37 38const periodList = [ 39 { 40 id: 1, 41 name: 'RED', 42 value: 'red' 43 }, 44 { 45 id: 2, 46 name: 'BLUE', 47 value: 'blue' 48 }, 49 { 50 id: 3, 51 name: 'GREEN', 52 value: 'green' 53 } 54] 55 56export default { 57 components: { 58 RadioButton 59 }, 60 data() { 61 return { 62 periodList: periodList, 63 checked1: 'blue', 64 radioButtonName1: 'my-radio1', 65 checked2: 'green', 66 radioButtonName2: 'my-radio2' 67 } 68 }, 69 methods: { 70 updateValue1(value) { 71 this.checked1 = value 72 }, 73 updateValue2(event) { 74 this.checked2 = event.target.value 75 } 76 } 77} 78</script>

以上参考になれば幸いです。

2020/05/13 追記

子コンポーネントのradioボタンで変更すると親コンポーネントに伝播します。
また、親コンポーネント側で直接プロパティを変えてもそれが子コンポーネントに伝播します。

vue

1<radio-button 2 v-for="period in periodList" 3 :key="`${radioButtonName1}-${period.id}`" 4 :content="period" 5 :name="radioButtonName1" 6 :checkedValue="checked1" 7 @change="updateValue1" 8></radio-button> 9<div class="data">value = {{ checked1 }}</div> 10<div> 11 <input type="text" v-model="checked1" /> 12</div>

イメージ説明

投稿2020/05/07 13:03

編集2020/05/13 09:32
rubytomato

総合スコア1752

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Apple_7777

2020/05/13 07:04

回答ありがとうございます。 こちら既に試したのですが、computedのsetterは内部での変更を検知できても、上から降ってくるpropsの変更を検知できず、propsのischeckedの値の変更が合った場合でも選択済みのラジオボタンが変化しませんでした。 また、watcherでは内部も外部(props等)の変更も検知できたので、watcherを使っていました。 もし、computedのsetterでも外部の変更を検知できるのであれば、ご回答頂いた方法で実装してみたいのですが、computedのsetterは外部からの変更も検知できるようなものなのでしょうか?
rubytomato

2020/05/13 09:41

どういう状況(どのようなコード)なのかちょっとわかりかねますが、回答に追記したとおり、親側で変えても、子側で変えても、親側で持つデータプロパティの値は変わります。 ただし、おっしゃられるとおり、親側のデータプロパティを直接変更すると、子側のセッターでは変更は検知できません。 どうしても検知したい場合は子側のwatchで監視することはできます。 watch: { checkedValue(val, newVal) { console.log(val, newVal) } }
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問