結論から
Vuetify 2.1.7で動作確認済みです。
<template>
<v-data-table
v-model="selected"
:headers="headers"
:items="items"
item-key="id"
show-select
>
<template v-slot:item="{ item, isSelected }">
<tr :class="tableRowClass(item, isSelected)">
<td>
<v-simple-checkbox :value="isSelected" @input="toggleRow(item)" class="v-data-table__checkbox" hide-details />
</td>
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ item.status }}</td>
</tr>
</template>
</v-data-table>
</template>
<script>
export default {
data () {
return {
headers: [
{ text: 'id', value: 'id', sortable: false },
{ text: 'name', value: 'name', sortable: false },
{ text: 'status', value: 'status', sortable: false }
],
selected: [],
}
},
methods: {
toggleRow (item) {
if (this.selected.includes(item)) {
// item が含まれていたら除去する
this.selected = this.selected.filter(v => v !== item)
} else {
// item を追加する
this.selected.push(item)
}
},
tableRowClass (item, isSelected) {
let klasses = []
if (item.status != 'active') klasses.push('grey') // status が active だったら tr に grey クラスを追加する
if (isSelected) klasses.push('v-data-table__selected') // checkbox が選択されていたら tr に v-data-table__selected クラスを追加する
return klasses.join(' ')
}
}
}
</script>
背景
Vuetify 2.x の v-data-table では 1.5.x に比べて設定できる slot が増え、テーブルの表現力が格段に上がりました。
1.5.x までは <template slot="items" slot-scope="props">
と tbody 以下をまるっと書いていたものは <template v-slot:body="{ items }"><tbody><tr v-for="item in items" :key="item.id">
や <template v-slot:item="{ item }">
と書くことができたり、<template v-slot:item.name="{ value }">
と個別の td 内のみを書くことができるようになっています。
ですが、今回 show-select
(チェックボックスでrowを選択できる機能) を使いつつ items の個々の item (要は tr タグ) に class を動的に追加したい要件が出てきて、いざ設定しようとしたときに、ドキュメントにそれっぽい slot も存在しないし、ソースコードを見てもいい感じに設定できそうな書き方が思い付かず、どうやって追加したらいいものか結構悩みました。やっと解決できたので、覚書として記事に残します。
解決まで
最初は vuetify 2 data-table row show-select checkbox
と Google で検索して出てきた
https://stackoverflow.com/questions/57671044/select-all-rows-of-a-vuetify-data-table-with-custom-table-body-implementation
↑この記事を参考に書き始めましたが、上手く行きませんでした。
以下に抜粋します。
<v-data-table v-model="selectedTasks" :headers="headers" :items="tasks" item-key="id" show-select>
<template v-slot:body="{ items }">
<tbody>
<tr v-for="item in items" :key="item.id">
<td>
<v-checkbox v-model="selectedTasks" :value="item" style="margin:0px;padding:0px" hide-details />
</td>
<td>{{ item.text }}</td>
<td>
<v-btn text icon x-small>
Edit
</v-btn>
</td>
</tr>
</tbody>
</template>
</v-data-table>
...
<script>
data() {
selectedTasks: []
}
</script>
上手く行かなかった点としては、
- v-checkboxの :value に
item
を指定しておいて、チェックボックスを押下すると、v-model のselectedTasks
に要素が追加されます(ここまでは問題なし)。この後v-paginationなどでテーブルのitemsが変化すると、selectedTasksには要素が一つしか無いにも関わらず、表示されたitemsが全て選択された(チェックボックスがチェック済みになっている)状態になっていた。 - (気持ちの問題かもしれませんが、) style属性でmargin、paddingを設定するのがちょっと嫌だな…と。
これらの問題については、なぜそうなるのか詳しいところまで調べてはいません。
解決のポイントは
-
<template v-slot:item="{ item, isSelected }">
-
item
slot を使う。 -
body
だと row ごとのisSelected
を取れない。
-
-
<v-simple-checkbox :value="isSelected" @input="toggleRow(item)" class="v-data-table__checkbox" hide-details />
-
v-checkbox
ではなくてv-simple-checkbox
を使う。 -
v-checkbox
だと、前後に (余計なタグやclassが挿入されてしまうため) margin や padding を調整しなければならない。 -
v-simple-checkbox
ならv-simple-checkbox
class のみの div タグが設定されて magin や padding の指定が不要。class="v-data-table__checkbox"
は Vuetify のソースコードでは付与しているので追加してみたが、特に表示上は有っても無くても変わらない模様。 -
<v-checkbox v-model="selectedTasks" :value="item">
で上手くいきそうな気がするが、上記の items が全て選択された(意図しない)状態になる問題が発生する。 -
v-simple-checkbox
の value に boolean を返すisSelected
を指定して、 input イベントで selected に item を toggle する method を発火させる。
-
ざっくりとこんな感じです。
もしかしたら他に slot を上手く使うもっといい方法があるのかもしれませんので、詳しい方がいらっしゃったら是非コメントを頂きたいです m(_ _)m