🗻

【Vue】チェックされた値を取得して任意の場所に出力する〜エリアと都道府県を用いた実装例〜

2024/06/26に公開

はじめに

Vue.jsを用いて、選択されたチェックボックスの値を取得して任意の場所に出力する方法を解説します。
イメージとしては、予約サイトの一休.comのエリア検索になります。

サンプル
一休.comのエリア検索。選択した条件が検索欄にタグ形式で出力される

実装内容の詳細

「エリア(東北とか関東)ごとに区切られた都道府県のチェックボックスがあり、チェックされたものの名前を取得して任意の場所に出力する」というのが基本的な実装内容になります。
ただし、以下の点に注意しなければなりません。

  • 直近で選択した要素が先頭に出力される
  • エリアと直下の都道府県の選択状態を連動させる
  • エリア内の都道府県全てにチェックがついた場合
    • 出力先にそのエリアの名前を追加し、代わりに該当エリア内の都道府県を削除する
  • エリア内の都道府県全てにチェックがついている状態から、一つでも外れた場合
    • 出力先からエリアの名前を削除する
    • チェックが外れたもの以外の全ての都道府県の名前を出力先に追加する

「配列にエリアや都道府県の名前を追加して、v-modelで出力させる」ということが出来れば楽なのですが、条件に応じてエリアと都道府県の選択状態を連動させなければいけないので、それらの細かい挙動を考慮すると別個でメソッドを用意してv-on:changeで始動させる必要がありそうです。

サンプルコード

HTMLとJavaScriptのサンプルコードを以下に記します
:::メッセージ
CSSは各々でご調整ください。
:::

index.html
<div id="app">
  <div class="tag">
    <ul class="tags__list">
      <li v-for="(tag, index) in tags" key="tag.value" class="tags__item">
        {{ tag.value }}
      </li>
    </ul>
  </div>
  <div class="area">
    <div v-for="area in areas" :key="area.label" class="area__section">
      <label :for="area.label">
        <input type="checkbox"
          :id="area.label"
          :name="area.label"
          v-model="area.checked"
          @change="changeArea(area)"
        >
        {{ area.label }}
      </label>
      <ul>
        <li v-for="prefecture in area.prefectures" :key="prefecture.label" class="l-area__item">
          <label :for="prefecture.label">
            <input type="checkbox"
              :id="prefecture.label"
              :name="area.label"
              v-model="prefecture.checked"
              @change="changePrefecture(area, prefecture)"
            >
            {{ prefecture.label }}
          </label>
        </li>
      </ul>
    </div>
  </div>
</div>
script.js
const app = Vue.createApp({
  data: () => ({
    areas: [
      // 北海道・東北
      {
        id: 1,
        label: '北海道・東北',
        checked: false,
        prefectures: [
          { label: '北海道', checked: false },
          { label: '青森', checked: false },
          { label: '岩手', checked: false },
          { label: '宮城', checked: false },
          { label: '秋田', checked: false },
          { label: '山形', checked: false },
          { label: '福島', checked: false },
        ]
      },
      // 甲信越・北関東
      {
        id: 2,
        label: '甲信越・北関東',
        checked: false,
        prefectures: [
          { label: '茨城', checked: false },
          { label: '栃木', checked: false },
          { label: '群馬', checked: false },
          { label: '新潟', checked: false },
          { label: '山梨', checked: false },
          { label: '長野', checked: false },
        ]
      },
      // 関東
      {
        id: 3,
        label: '関東',
        checked: false,
        prefectures: [
          { label: '埼玉', checked: false },
          { label: '千葉', checked: false },
          { label: '東京', checked: false },
          { label: '神奈川', checked: false },
        ]
      },
      // 以下省略...
    ],
    tags: [],
  }),
  methods: {
    // 配列に名前を追加・削除するメソッド
    createTag(value) {
      this.tags.unshift({ id: value, value });
    },
    removeTag(value) {
      this.tags = this.tags.filter(tag => tag.value !== value);
    },
    // エリア・都道府県のチェック状態が変更された場合のメソッド
    // エリア
    changeArea(area) {
      if (area.checked) {
        this.createTag(area.label);
        // その地域に関連するすべての都道府県にもチェックを付ける
        area.prefectures.forEach(prefecture => {
          prefecture.checked = true;
        });
      } else {
        this.removeTag(area.label);
        // その地域に関連するすべての都道府県のチェックも外す
        area.prefectures.forEach(prefecture => {
          prefecture.checked = false;
          this.removeTag(prefecture.label);
        });
      }
    },
    // 都道府県
    changePrefecture(area, prefecture) {
      if (prefecture.checked) {
        this.createTag(prefecture.label);
        area.checked = true;
        // そのエリアのすべての都道府県にチェックが付いている場合、エリアのチェックも付ける
        const allChecked = area.prefectures.every(pref => pref.checked);
        if (allChecked) {
          area.checked = true;
          // そのエリアのタグを出力し、代わりにそのエリアの都道府県のタグを削除する
          this.createTag(area.label);
          area.prefectures.forEach(pref => {
            this.removeTag(pref.label);
          });
        }
      } else {
        this.removeTag(prefecture.label);
        // チェックが付いているprefectureの数が (そのareaのprefecutreの数 - 1) の場合
        // そのprefectureのareaのタグを削除し、チェックが外された場所以外のprefectureを生成
        const checkedCount = area.prefectures.filter(pref => pref.checked).length;
        if (checkedCount === area.prefectures.length - 1) {
          this.removeTag(area.label);
          area.prefectures.forEach(pref => {
            if (pref.checked && pref.label !== prefecture.label) {
              this.createTag(pref.label);
            }
          });
        } else if (checkedCount === 0) {
          // そのareaのprefecture全てのチェックが外れた場合
          area.checked = false;
        }
      }
    },
  }
})
app.mount('#app');

解説

JavaScript

まずは2つの配列を準備します。

  • areas:エリア・都道府県のデータを格納します。ラベルやチェック状態などの情報を持たせます。
  • tags:初期状態では空の配列です。この配列にチェックされた要素を追加して、その値をリアルタイムで出力します。

次に、選択されたエリア・都道府県を配列に追加・配列から削除するメソッドを用意しましょう。
unshiftで要素を配列の先頭に追加、filterで条件に一致する要素を配列から削除することができますので、それぞれ個別にメソッドを用意してあげましょう。チェックボックスの選択状態が変化したタイミングで、条件に応じて専用のメソッドを始動させます。

最後に、エリア・都道府県の選択状態が変化した場合のメソッドを用意します。
エリアの場合はシンプルで、自身をtags配列に追加/削除して、直下の都道府県全てのチェックをつけ外しするだけです。area.checked を条件として、分岐に応じてメソッドを始動させましょう。

都道府県の場合は少し複雑になります。自身のtags配列への追加と削除、チェック状態の切り替えはエリアと同様ですが、同じエリアの都道府県のチェック状況を監視して、それに応じて個別にメソッドを始動させなければなりません。
そのエリアのすべての都道府県がチェックされた場合、エリアの名前を配列に追加してチェックも付けると同時に、都道府県の名前を配列から削除します。
const allCheckedの真偽値ですべての都道府県がチェックされているかを判定しましょう。trueだった場合、createTag(area.label)でエリアの名前を配列に追加、forEachを回してエリア内の都道府県全ての名前でthis.removeTag(pref.label)を実行します。

最後に、全て選択された状態から一つでもチェックが外れた場合です。const checkedCountでチェックされた要素の数を取得しておきましょう。
checkedCount === area.prefectures.length - 1が成り立つ場合に、エリアの名前を配列から削除すると同時に、それ以外の都道府県の名前を配列に追加します。また、checkedCount === 0が成り立つ場合は全てのチェックが外れたということになりますので、area.checkedをfalseにしてチェックを外してあげましょう。

HTML

必要なセクションとして、「選択されたエリア・都道府県の名前を出力する場所」と、「エリア・都道府県のチェックボックスが並ぶ場所」の2つが必要です。
それぞれのセクションで、js側で用意した配列をv-forで出力します。また、エリア・都道府県のチェックボックスに対して、v-modelでチェック状態・v-on:changeでイベントハンドラを設定するようにしましょう(省略記法使ってます)

https://v2.ja.vuejs.org/v2/guide/components#コンポーネントで-v-model-を使う

終わりに

エリアと都道府県を例として、Vue.jsでチェックボックスの値を取得し任意の場所に出力する方法を解説しました。今回はチェックボックスの状態を連動させなければならなかったため少し複雑なコードになり、また先述したようにv-modelとv-onの併用について最適化できる余地があります。
改善点や解決策などありましたら、ぜひお知らせください🙇

Discussion