前提
なにがしたいの?
Vue.jsを利用していて、単品RadioButtonでことが済んでいたんだけれど(サーバーサイドテンプレートエンジンに対して、UIパーツを作る形でVue.jsを使っているプロジェクトで、formのpostとかjsでやってないし)
どうしても、RadioButtonGroupが欲しくなった。
なんで?
- RadioButtonパーツのHTMLの構成を変えたくなかった
<label class="radio">
<input type="radio">
<span class="radio-label">ラベル表示</span>
<span class="radio-icon">まるポチ的なやつ表示用</span>
</label>
-
まるポチではない見た目でUIを作ろうとしたら、labelに状態classをつけるために、あるモードが選択された後、他のモードを選択されることによって、別の子がオンになったよ~な通知が必要になった。
-
とはいえ、その通知はラジオボタンの話なので、マウント元にその切替用のことをいろいろ書きたくなかった。
他に言いたいことは
- でも、ラジオボタン自体はマウント元に書きたい。
つまり、こんなカンジにしたい。
<div hoge="わたしはマウント元">
<radio-button-group>
<v-radio-button props="props" />
<v-radio-button props="props" />
<v-radio-button props="props" />
</radio-button-group>
</div>
。。。なんか、文章がへたくそね。
で、やったこと。
いろいろ検索する
-
Vue.jsでラジオボタン [radio button]
https://noumenon-th.net/programming/2019/05/31/radio/ -
Vue.jsのScoped Slotを使って脱初心者なラジオボタンコンポーネントを作るまで
https://qiita.com/simezi9/items/702e2bd066eec352ef72 -
上記でも紹介されてますが、MaterialUIやVuetifyの実装もちょっと見てみました。
…さらっとだけでよくわかってないけど。
でもね
なんか、思ったようなものにならず。(そんな難しいことできないし。)
自分なりの実装をしてみました。
ここからが、本当にやったこと
作成前提
そもそも、こんなRadioButtonコンポーネントがあります。
<template>
<label class="radio">
<input type="radio"
:name="name"
:value="value"
:required="is_required"
v-model="selectedItem"
/>
<span class="radio-icon"></span>
<span class="radio-button-label">{{ label }}</span>
</label>
</template>
<script>
export default {
name: 'ElRadioButton',
props: {
name: { type: String }, //name属性値
value: { type: String }, //value属性値
is_required: { type: Boolean }, //必須項目か
is_defSelected: { type: Boolean }, //初期状態でチェックされているか
label: { type: String } //表示項目名
},
computed: {
selectedItem: {
get: function() {
if ( this.is_defSelected) {
return this.value
}
},
set: function(val) {
this.$emit('change', val)
}
},
}
}
</script>
で、シンプルにこれをマウントするには
<div>
<el-radio-button
v-for="(menu, index) in menus"
:key="index"
name = "menutype"
:value = "menu.name"
:is_def-selected = "最初に選択されているものの条件式でboolean値"
:is_required = false
:label = "menu.label"
@change = "changeRadio"
></el-radio-button>
</div>
<script>
import ElRadioButtonGroup from '@/components/element/RadioButtonGroup'
import ElRadioButton from '@/components/element/RadioButton'
export default {
name: 'TestPage',
components: {ElRadioButtonGroup, ElRadioButton},
data() {
return {
menus: ラジオボタンの中身が記載されたJSON,
}
},
methods: {
changeRadio(d) {
// d: 現在選択されているradioButtonのvalue
}
}
}
</script>
で、なにをもっても、RadioButtonGroupの作成
<template>
<div>
<slot></slot>
</div>
</template>
<script>
export default {
name: 'ElRadioButtonGroup'
}
</script>
実際のRadioButtonはマウント元で記載する(propsとして、データを引き継がない)ので、<slot>
を準備します。
で、親コンポーネントにマウント
<template>
<div>
<el-radio-button-group>
<el-radio-button
v-for="(menu, index) in menus"
:key="index"
name = "menutype"
:value = "menu.name"
:is_def-selected = "最初に選択されているものの条件式でboolean値"
:is_required = false
:label = "menu.label"
@change = "changeRadio"
></el-radio-button>
</el-radio-button-group>
</div>
</template>
<script>
import ElRadioButtonGroup from '@/components/element/RadioButtonGroup'
import ElRadioButton from '@/components/element/RadioButton'
export default {
name: 'Parent',
components: {ElRadioButtonGroup, ElRadioButton},
data() {
return {
menus: ラジオボタンの中身が記載されたJSON,
}
},
methods: {
changeRadio(d) {
// d: 現在選択されているradioButtonのvalue
}
}
}
</script>
RadioButtonGroupにも、今なにが選択されているのか教える
RadioButtonGroup⇒それぞれのRadioButtonにそれぞれどの状態になっていて欲しいか通知するため、RadioButtonGroupに、現状を教える
<template>
<div>
<el-radio-button-group :value="menu_now">
<el-radio-button
v-for="(menu, index) in menus"
~省略~
@change = "changeRadio"
></el-radio-button>
</el-radio-button-group>
</div>
</template>
<script>
~省略~
data() {
return {
menus: ラジオボタンの中身が記載されたJSON,
menu_now: デフォルトのメニューのvalueが入るようにする
}
},
methods: {
changeRadio(d) {
this.menu_now = d
}
}
}
</script>
RadioButtonGroupからRadioButtonへ通達
<template>
~省略~
</template>
<script>
export default {
props: ['value'],
watch: {
value(nv) {
this.$children.forEach(item => {
if (item.value == nv) {
item.is_selected = true
} else {
item.is_selected = false
}
})
}
},
}
</script>
RadioButtonで受け取って、class名追加
<template>
<label class="radio" :class="{ 'is_selected' : is_selected}">
~省略~
</label>
</template>
<script>
~省略~
data() {
return {
is_selected: this.is_defSelected //最初は初期値の設定を受け取る
}
},
computed: {
selectedItem: {
get: function() {
if ( this.is_selected ) { //デフォルト設定ではなく、変更通知の受け取りフラグをもとにする
return this.value
}
},
~省略~
</script>
まとめ
RadioButtonのチェック状態(is_defSelected)をBoolean値で持たせたことが、不幸のはじまりのような気がしました。