Loading
BLOG 開発者ブログ

2021年3月25日

Vue.jsで選択した項目によって表示内容が変わるtableを作ってみよう

はじめに

選択した項目によってtableの表示内容を切り替えたい。

さらにtable要素内のthead要素を固定してtbody要素をスクロール可能にしたい。

今回はそんな場合の対応方法をVue.jsで考えてみました。

こんにちは。クラウドソリューショングループのtakinami.sです。

というわけで、上の画像のような表を作っていきます。

 

動作確認環境

vue: 2.6.12

bootstrap: 4.5.0

 

今回は.vue拡張子の単一ファイルコンポーネントで書いていきます。

 

コードを見ていく

<script>の中身

単一ファイルコンポーネントでは<template>,<script>,<style>の順番で書いていかなくてはいけませんが、画面に表示させるデータを先に確認したいのでまずは<script>の中を見ていきます。

export default {
  data() {
    return {
      isActive: 0,
      foods: [
        {
          category: "野菜",
          kinds: [
            {
              name: "じゃがいも",
              place: "北海道"
            },
            {
              name: "人参",
              place: "北海道"
            },
            {
              name: "大根",
              place: "神奈川県"
            },
            {
              name: "トマト",
              place: "熊本県"
            },
          ]
        },
        {
          category: "肉",
          kinds: [
            {
              name: "豚肉",
              place: "鹿児島県"
            },
            {
              name: "鶏肉",
              place: "宮崎県"
            },
            {
              name: "牛肉",
              place: "北海道"
            },
            {
              name: "猪肉",
              place: "兵庫県"
            },
          ]
        },
        {
          category: "果物",
          kinds: [
            {
              name: "りんご",
              place: "青森県"
            },
            {
              name: "梨",
              place: "千葉県"
            },
            {
              name: "みかん",
              place: "和歌山県"
            },
            {
              name: "桃",
              place: "山梨県"
            },
            {
              name: "りんご",
              place: "青森県"
            },
            {
              name: "梨",
              place: "千葉県"
            },
            {
              name: "みかん",
              place: "和歌山県"
            },
            {
              name: "桃",
              place: "山梨県"
            },
            {
              name: "りんご",
              place: "青森県"
            },
            {
              name: "梨",
              place: "千葉県"
            },
            {
              name: "みかん",
              place: "和歌山県"
            },
            {
              name: "桃",
              place: "山梨県"
            },
          ]
        },
      ]
    }
  }
}

foodsという配列の中に項目として野菜・肉・果物を取り上げ、それぞれの種類と産地をまとめてみました。

isActiveではボタンでどの項目が選択されているのかを見ています。初期状態では0、つまり一番最初の項目が選択されている状態です。今回の場合は野菜が選択されていることになります。

 

<template>の中身

次に<template>の中身を見ていきましょう。

 

項目選択用のボタンはこのようになっています。

<div class="btn-group btn-group-toggle mb-3">
  <label class="btn btn-outline-primary" 
         v-for="(btnItem, key) in foods" :key="key" 
         :class="[key == 0 ? {active: isActive == key} : '', {active: isActive == key}]">
    <input type="radio" :value="key" v-model="isActive">{{ btnItem.category }}
  </label>
</div>

1つ目のポイントは

v-for="(btnItem, key) in foods" :key="key"

の部分です。

v-forを使ってリストレンダリングをしています。さらに、項目の順番が入るkeyを設定しています。

次のポイントは

:class="[key == 0 ? {active: isActive == key} : '', {active: isActive == key}]">

の部分です。

keyには最初は0を入れ、<script>内で記述したisActiveの値とkeyの値が一致したらactiveというclassを追加するように設定しています。

 

続いて表の部分のコードも見ていきましょう。

<div class="cover-table">
  <table class="table table-border border mb-0 food-table">
    <thead>
      <tr>
        <th class="food-name border-right">名前</th>
        <th class="food-place">産地</th>
      </tr>
    </thead>
  </table>

  <div class="overflow-auto table-body" v-for="(item, key) in foods" :key="key">
    <table class="table table-border border food-table" v-show="isActive == key">
      <tbody>
        <tr v-for="(tableItem, key) in item.kinds" :key="key">
          <td class="food-name border-right">{{ tableItem.name }}</td>
          <td class="food-place">{{ tableItem.place }}</td>
        </tr>
      </tbody>
    </table>
  </div>
</div>

Vue,jsを使うところで注目してほしいのは、

<div class="overflow-auto table-body" v-for="(item, key) in foods" :key="key">
<table class="table table-border border food-table" v-show="isActive == key">
<tr v-for="(tableItem, key) in item.kinds" :key="key">

この3点です。

表の部分でもv-forのkeyを設定し、isActiveとkeyの値が一致したときに対応するtableを表示させています。

これでボタンで選択した項目によって、tableの表示内容を切り替えていくことができるようになりました。

 

さて、table全体を覆う

<div class="cover-table">

があることと、thead要素とtbody要素で別々のtable要素を使っていることに気がついたでしょうか。

これらがtbody要素のみをスクロールさせるときに活きてきます。

tbody要素を含むtableのほうにはbootstrapのoverflow-autoクラスを設定し、スクロールを可能にしています。

 

<style>の中身

<style scoped>
  .cover-table {
    width: 418px;
  }
  .table-body {
    max-height: 200px;
  }
  .food-table {
    width: 400px;
  }
  .food-name, .food-place {
    width: 200px;
  }
</style>

今回はtable全体を覆うcover-tableクラスには418pxの幅を、tbody要素を含むtable要素のfood-tableクラスには400pxの幅を設定しました。

18pxの差がありますが、これはレイアウトが崩れないようスクロールバーの幅を考慮に入れているためです。

tbody要素の最大の高さは200pxに設定、セルの幅も200pxに設定しました。

これでtbody要素が200pxを超えるときにはtbody要素がスクロールできるようになります。

 

最後に

今回はbootstrapを使いましたが、もちろんスタイルはCSSで自分で設定することもできます。

最後に今回のコードの全体を載せておきます。

<template>
  <div>
    <div class="btn-group btn-group-toggle mb-3">
      <label class="btn btn-outline-primary" v-for="(btnItem, key) in foods" :key="key" :class="[key == 0 ? {active: isActive == key} : '', {active: isActive == key}]">
        <input type="radio" :value="key" v-model="isActive">{{ btnItem.category }}
      </label>
    </div>

    <div class="cover-table">
      <table class="table table-border border mb-0 food-table">
        <thead>
          <tr>
            <th class="food-name border-right">名前</th>
            <th class="food-place">産地</th>
          </tr>
        </thead>
      </table>

      <div class="overflow-auto table-body" v-for="(item, key) in foods" :key="key">
        <table class="table table-border border food-table" v-show="isActive == key">
          <tbody>
            <tr v-for="(tableItem, key) in item.kinds" :key="key">
              <td class="food-name border-right">{{ tableItem.name }}</td>
              <td class="food-place">{{ tableItem.place }}</td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>

  </div>
</template>

<script>
export default {
  data() {
    return {
      isActive: 0,
      foods: [
        {
          category: "野菜",
          kinds: [
            {
              name: "じゃがいも",
              place: "北海道"
            },
            {
              name: "人参",
              place: "北海道"
            },
            {
              name: "大根",
              place: "神奈川県"
            },
            {
              name: "トマト",
              place: "熊本県"
            },
          ]
        },
        {
          category: "肉",
          kinds: [
            {
              name: "豚肉",
              place: "鹿児島県"
            },
            {
              name: "鶏肉",
              place: "宮崎県"
            },
            {
              name: "牛肉",
              place: "北海道"
            },
            {
              name: "猪肉",
              place: "兵庫県"
            },
          ]
        },
        {
          category: "果物",
          kinds: [
            {
              name: "りんご",
              place: "青森県"
            },
            {
              name: "梨",
              place: "千葉県"
            },
            {
              name: "みかん",
              place: "和歌山県"
            },
            {
              name: "桃",
              place: "山梨県"
            },
            {
              name: "りんご",
              place: "青森県"
            },
            {
              name: "梨",
              place: "千葉県"
            },
            {
              name: "みかん",
              place: "和歌山県"
            },
            {
              name: "桃",
              place: "山梨県"
            },
            {
              name: "りんご",
              place: "青森県"
            },
            {
              name: "梨",
              place: "千葉県"
            },
            {
              name: "みかん",
              place: "和歌山県"
            },
            {
              name: "桃",
              place: "山梨県"
            },
          ]
        },
      ]
    }
  }
}
</script>

<style scoped>
.cover-table {
  width: 418px;
}
.table-body {
  max-height: 200px;
}
.food-table {
  width: 400px;
}
.food-name, .food-place {
  width: 200px;
}
</style>

 

 

takinamisのブログ