LoginSignup
2
2

More than 3 years have passed since last update.

Vue.jsでRadioButtonGroupコンポーネントを自作する

Posted at

前提

なにがしたいの?

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>

。。。なんか、文章がへたくそね。

で、やったこと。

いろいろ検索する

でもね

なんか、思ったようなものにならず。(そんな難しいことできないし。)
自分なりの実装をしてみました。

ここからが、本当にやったこと

作成前提

そもそも、こんなRadioButtonコンポーネントがあります。

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>

で、シンプルにこれをマウントするには

parent
<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の作成

RadioButtonGroup
<template>
  <div>
    <slot></slot>
  </div>
</template>

<script>
export default {
  name: 'ElRadioButtonGroup'
}
</script>

実際のRadioButtonはマウント元で記載する(propsとして、データを引き継がない)ので、<slot>を準備します。

で、親コンポーネントにマウント

parent
<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に、現状を教える

parent
<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へ通達

RadioButtonGroup
<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名追加

RadioButton
<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値で持たせたことが、不幸のはじまりのような気がしました。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2