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>の中を見ていきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
export default { data() { return { isActive: 0, foods: [ { category: "野菜", kinds: [ { name: "じゃがいも", place: "北海道" }, { name: "人参", place: "北海道" }, { name: "大根", place: "神奈川県" }, |
foodsという配列の中に項目として野菜・肉・果物を取り上げ、それぞれの種類と産地をまとめてみました。
isActiveではボタンでどの項目が選択されているのかを見ています。初期状態では0、つまり一番最初の項目が選択されている状態です。今回の場合は野菜が選択されていることになります。
<template>の中身
次に<template>の中身を見ていきましょう。
項目選択用のボタンはこのようになっています。
1 2 3 4 5 6 7 |
<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つ目のポイントは
1 |
v-for="(btnItem, key) in foods" :key="key" |
の部分です。
v-forを使ってリストレンダリングをしています。さらに、項目の順番が入るkeyを設定しています。
次のポイントは
1 |
:class="[key == 0 ? {active: isActive == key} : '', {active: isActive == key}]"> |
の部分です。
keyには最初は0を入れ、<script>内で記述したisActiveの値とkeyの値が一致したらactiveというclassを追加するように設定しています。
続いて表の部分のコードも見ていきましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<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を使うところで注目してほしいのは、
1 |
<div class="overflow-auto table-body" v-for="(item, key) in foods" :key="key"> |
1 |
<table class="table table-border border food-table" v-show="isActive == key"> |
1 |
<tr v-for="(tableItem, key) in item.kinds" :key="key"> |
この3点です。
表の部分でもv-forのkeyを設定し、isActiveとkeyの値が一致したときに対応するtableを表示させています。
これでボタンで選択した項目によって、tableの表示内容を切り替えていくことができるようになりました。
さて、table全体を覆う
1 |
<div class="cover-table"> |
があることと、thead要素とtbody要素で別々のtable要素を使っていることに気がついたでしょうか。
これらがtbody要素のみをスクロールさせるときに活きてきます。
tbody要素を含むtableのほうにはbootstrapのoverflow-autoクラスを設定し、スクロールを可能にしています。
<style>の中身
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<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で自分で設定することもできます。
最後に今回のコードの全体を載せておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
<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> |