「チェックボックスで選択できる数を3つまでに制限したい!」など、
チェックボックスの選択数を制限したい時の
Vue.js(Vue3 / Composition API)での記述方法をご紹介します。
チェックボックスの選択数制限
コード完成版
<script setup>
import { ref } from 'vue'
// 補足1参照
const selectedOptions = ref([])
// 下記optionsは別ファイルに切り出すことをお勧めしますが、ここでは同じファイル内に記述します
const options = [
{ id: 1, value: '選択肢1' },
{ id: 2, value: '選択肢2' },
{ id: 3, value: '選択肢3' },
{ id: 4, value: '選択肢4' },
{ id: 5, value: '選択肢5' },
{ id: 6, value: '選択肢6' },
]
// ここでは最大選択数を3とします
const maxNumber = 3
// 補足2参照
const maxSelection1 = (index) => (
selectedOptions.value.length >= maxNumber && !selectedOptions.value.includes(index)
)
// 別の記述方法
const maxSelection2 = (index) => (
selectedOptions.value.length >= maxNumber && selectedOptions.value.indexOf(index) === -1
)
</script>
<template>
<div class="margin">
<div class="flex">
<!-- 補足3参照 -->
<div
v-for="(option, index) in options"
:key="option">
<!-- 補足4参照 -->
<label
:for="`option_${index}`"
:class="{ 'opacity-25' : maxSelection1(index) }">
<input
v-model="selectedOptions"
:id="`option_${index}`"
type="checkbox"
:value="index"
:disabled="maxSelection1(index)" />
{{ option.value }}
</label>
</div>
</div>
</div>
</template>
<style>
/* cssは別ファイルからの読み込みを推奨しますが、ここでは同じファイル内に記述します */
.margin {
margin: 2em;
}
.flex {
display: flex;
gap: 10px;
margin: 5px 0;
}
.opacity-25 {
opacity: 0.25;
}
</style>
各セクションの補足説明です。
- scriptタグ内 -
import { ref } from 'vue'
const selectedOptions = ref([])
チェックボックスで選択された値を格納する配列"selectedOptions"を
リアクティブ(値が更新された時に変更が検知される状態)にするため、
ref関数を使用しています。
- 参考
// ここでは最大選択数を3とします
const maxNumber = 3
const maxSelection1 = (index) => (
selectedOptions.value.length >= maxNumber && !selectedOptions.value.includes(index)
)
// 別の記述方法
const maxSelection2 = (index) => (
selectedOptions.value.length >= maxNumber && selectedOptions.value.indexOf(index) === -1
)
選択数制限判定の関数を2つ紹介しています。
1つ目の記述方法は、includesメソッドを使用する記述です。
maxSelection1関数では、以下2つの条件がどちらもtrueの場合にtrueを返します。
- 選択された値が入っている配列(selectedOptions)の要素の数が
maxNumber
以上 - 引数で渡ってきた
index
がselectedOptionsの要素に含まれていない(つまりチェックボックスが選択されていないということ)
※関数を実行している部分は、補足4で説明しているので、
trueを返すことでどのような動きになっているかは、補足4を確認してください。
2つ目の記述方法は、indexOfメソッドを使用する記述です。
こちらは、1つ目の記述方法と条件としては同じですが、
"選択されていない"ことを判定する方法として、
indexOfメソッドの「引数に与えられた内容が配列に存在しない場合は -1 を返す」
という特徴を利用した記述になっています。
1、2どちらでも結果は同じですが、パフォーマンス比較だとincludesメソッドの方が高速、
コードの読みやすさからもincludesメソッドの方が個人的にはお勧めです。
ちなみに、scriptタグ内でrefは「.value」でアクセスすることが可能なので、
selectedOptions.value
となっています。
- 参考
- templateタグ内 -
<div
v-for="(option, index) in options"
:key="option">
v-forを使って、options(選択肢の配列)からデータを取り出し、繰り返しで表示します。
今回、配列のindexも利用したいので、 2つ目のエイリアスを指定しています。
<label
:for="`option_${index}`"
:class="{ 'opacity-25' : maxSelection1(index) }">
<input
v-model="selectedOptions"
:id="`option_${index}`"
type="checkbox"
:value="index"
:disabled="maxSelection1(index)" />
{{ option.value }}
</label>
input要素にv-modelを利用することで、入力値とデータを同期することができます。
チェックした順にvalue属性の値が配列"selectedOptions"に追加されていきます。
そして、maxSelection1関数(選択数制限判定の関数)でチェックボックスの制御を実行しています。
- 選択された値が入っている配列(selectedOptions)の要素の数が
maxNumber
以上 - チェックボックスが選択されていない
上記2つの条件がtrueである場合、
label要素にopacity: 0.25
のclass属性と、input要素にdisabled属性を追加します。
そうすることで、上限数に達した時点でチェックが入っていないチェックボックスは
選択できないように&視覚的にも選択できないことが分かるようにしています。
(チェックが入っているボックスは押下できるようにすることで、チェックの解除、
チェックの付け直しを実現しています)
また、label要素とinput要素を明示的に関連付けるために、
input要素にid属性を、label要素にfor属性を追加しています。
(forとidの値は同じにします)
または、inputをlabelの入れ子にすることでも、関連づけることが可能です。
その場合、forおよびid属性は必要ありません。
(上記ではforおよびidを追加していますが、追加しなくても同じく動作します)
label要素とinput要素を関連付けることで、チェックボックス部分だけでなく、
関連するラベル(テキスト)部分をクリックしてもチェックが入るようになります。
以上です!
読んでいただきありがとうございました^^