Adding class to <td> and <tr> in Vuetify data-table? - vue.js

<v-data-table
class="elevation-1 user-table"
:headers="table.headers"
:items="usersList"
:items-per-page="table.options.itemsPerPage"
:no-data-text="table.options.noDataText"
:footer-props="table.options.footerProps"
>
<template slot="items" slot-scope="props">
<td class="text-xs-right pa-4">{{ props.item.id }}</td>
<td class="text-xs-right pa-0">{{ props.item.username }}</td>
<td class="text-xs-right pa-0">{{ props.item.createdAt }}</td>
</template>
</v-data-table>
i try to use the slot="items" to change the class of td in vuetify table ,but it doesnt work?
i use this version of vuetify
"vuetify": "^2.3.17",

refactor item slot to this
<v-data-table
class="elevation-1 user-table"
:headers="table.headers"
:items="usersList"
:items-per-page="table.options.itemsPerPage"
:no-data-text="table.options.noDataText"
:footer-props="table.options.footerProps"
>
<template v-slot:item="{ item }">
<tr>
<td class="text-xs-right pa-4"> {{ item.id }} </td>
<td class="text-xs-right pa-0"> {{ item.username }} </td>
<td class="text-xs-right pa-0"> {{ item.createdAt }} </td>
</tr>
</template>
</v-data-table>
whats wrong with your code:
you used items instead of item => <template slot="item" slot-scope="props">
you missed tr element

Related

transition-group without wrapping in a span

I am trying to apply a transition to a Vuetify v-data-table so that when I delete a tbody in my table, it fades out instead of just vanishing off the screen. If I wrap the tbody in a transition-group the tbody fades out as expected. However, it's wrapped in a span tag which means all of the columns in the tbody get shoved into the first column of my table.
I know I can specify a different tag other than a span, but they all cause my table layout to break. How can I apply a fade transition to the tbody and maintain my table layout?
<v-data-table
ref="dataTable"
:headers="headers"
fixed-header
:height="height"
:items="invoices"
item-key="id"
:search="search"
class="elevation-1"
disable-pagination
:loading="loading"
loading-text="Loading invoices... please wait..."
:no-data-text="this.noDataText"
no-results-text="No invoices found for your search."
:hide-default-footer="true"
>
<template v-slot:headers="props">
<thead>
<tr>
<th
v-for="header in props.headers"
:key="header.text"
ref="dataTableHdr"
:class="['column sortable', pagination.descending ? 'desc' : 'asc', header.value === pagination.sortBy ? 'active' : '', header.class === '']"
#click="changeSort(header.value)"
>
<v-icon small>arrow_upward</v-icon>
{{ header.text }}
</th>
</tr>
</thead>
</template>
<template v-slot:body="{ items }">
<transition-group tag="boo" name="invoice">
<tbody v-for="(item, index) in items" :key="item.id" #click="items.splice(index,1)">
<tr>
<td
:rowspan="updateRowCount(item.invoiceSections)"
valign="top"
class="pt-3 text-left font-weight-medium"
>{{ item.vendorCode }}</td>
<td>CONTENT</td>
<td>CONTENT</td>
<td>CONTENT</td>
<td>CONTENT</td>
<td>CONTENT</td>
</tr>
<tr>
<td>{{ item.vendorName }}</td>
<td>CONTENT</td>
<td>CONTENT</td>
<td>CONTENT</td>
<td>CONTENT</td>
<td>CONTENT</td>
</tr>
</tbody>
</transition-group>
</template>
</v-data-table>
Revised Code:
<template v-slot:body="{ items }">
<tbody v-for="(item, index) in items" :key="item.id">
<transition-group tag="tbody" name="invoice">
<tr key="row1">
<td
:rowspan="updateRowCount(item.invoiceSections)"
valign="top"
class="pt-3 text-left font-weight-medium"
>{{ item.vendorCode }}</td>
<td>CONTENT</td>
<td>CONTENT</td>
<td>CONTENT</td>
<td>CONTENT</td>
<td>
<v-btn #click="reject(items, index)" class="mx-2" v-blur fab small>
<v-icon color="red">mdi-cancel</v-icon>
</v-btn>
</td>
</tr>
<tr key="row2">
<td>{{ item.vendorName }}</td>
<td>CONTENT</td>
<td>CONTENT</td>
<td>CONTENT</td>
<td>CONTENT</td>
<td>CONTENT</td>
</tr>
</transition-group>
</tbody>
</template>
<style>
.invoice-leave-active tr,
.invoice-leave-active tr:hover {
background-color: yellow !important;
}
.invoice-enter-active,
.invoice-leave-active {
transition: opacity 1s;
}
.invoice-enter, .invoice-leave-to /* .invoice-leave-active below version 2.1.8 */ {
opacity: 0;
}
</style>
Here is my final, super complex template, using v-if with changing the item.id to null.
<template v-slot:body="{ items }">
<transition-group tag="tbody" name="invoice" v-for="(item, index) in items" :key="index">
<tr
key="row1"
v-if="item.id"
:class="[item.duplicate ? 'duplicateItemRow' : '', {'success': item.id===selectedId}]"
>
<td
:rowspan="updateRowCount(item.invoiceSections)"
valign="top"
class="pt-3 text-left font-weight-medium"
>{{ item.vendorCode }}</td>
<td
:rowspan="updateRowCount(item.invoiceSections)"
valign="top"
class="pt-3 text-left font-weight-medium"
>{{ item.vendorName }}</td>
<td
:rowspan="updateRowCount(item.invoiceSections)"
valign="top"
class="pt-3 text-left font-weight-medium nowrap"
>{{ item.poNumber }}</td>
<td
:rowspan="updateRowCount(item.invoiceSections)"
valign="top"
class="pt-3 text-left font-weight-medium"
>
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<a
:href="`/rvimain.pgm?rqstyp=senchcall1&isys=a&i7=INV&i4=${item.invoiceNumber}&i6=${item.poNumber}`"
class="pa-0 ma-0"
v-on="on"
target="_blank"
>{{ item.invoiceNumber }}</a>
</template>
<span>Click to view invoice</span>
</v-tooltip>
</td>
<td
:rowspan="updateRowCount(item.invoiceSections)"
valign="top"
class="pt-3 pr-6 text-right font-weight-medium"
>{{ item.invoiceDate | moment("M/D/YYYY") }}</td>
<td :class="item.invoiceAmount !== item.poAmount ? 'amountMismatchItemRow' : ''"></td>
<td
valign="top"
:class="['pt-3', 'font-weight-medium', 'nowrap', item.invoiceAmount !== item.poAmount ? 'amountMismatchItemRow' : '']"
>
<div v-if="item.duplicate">{{ item.duplicate }}</div>
<div
v-if="item.invoiceAmount !== item.poAmount"
>${{ item.poAmount | currency }} PURCHASE ORDER DOES NOT MATCH INVOICE</div>
</td>
<td :class="item.invoiceAmount !== item.poAmount ? 'amountMismatchItemRow' : ''"></td>
<td :class="item.invoiceAmount !== item.poAmount ? 'amountMismatchItemRow' : ''"></td>
<td :class="item.invoiceAmount !== item.poAmount ? 'amountMismatchItemRow' : ''"></td>
<td :class="item.invoiceAmount !== item.poAmount ? 'amountMismatchItemRow' : ''">
<CommentsDialog
v-if="item.invoiceSections[0].comments"
:itemId="item.invoiceSections[0].id"
:comments="item.invoiceSections[0].comments"
/>
</td>
<td
valign="top"
:class="['pt-3', 'text-right', 'font-weight-medium', 'nowrap', item.invoiceAmount !== item.poAmount ? 'amountMismatchItemRow' : '']"
>{{ item.invoiceAmount | currency }}</td>
<td
:rowspan="updateRowCount(item.invoiceSections)"
valign="top"
class="pt-3 text-center"
>
<div v-if="item.approvalDate && !item.rejectedDate">
<v-icon color="green">mdi-check</v-icon>
</div>
<div
v-if="!item.duplicate && !item.approvalDate && !item.rejectedDate && item.invoiceAmount === item.poAmount"
class="mb-4"
>
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-btn #click="approveInvoice(item)" v-on="on" v-blur class="mx-2" fab small>
<v-icon color="green">mdi-check</v-icon>
</v-btn>
</template>
<span>Approve invoice</span>
</v-tooltip>
</div>
<RejectButton
v-if="!item.approvalDate && !item.rejectedDate"
:invoice="item"
#removeInvoice="removeInvoice"
/>
<div v-if="!item.approvalDate && item.rejectedDate">
<v-icon color="red" class="mb-4">mdi-cancel</v-icon>
<div>
Rejected
<br />
{{item.rejectedDate | moment("M/D/YYYY")}}
<br />
{{ item.rejectedBy }}
<br />
<RejectReasonDialog
v-if="item.rejectedReason"
:itemId="item.id"
:reason="item.rejectedReason"
/>
</div>
</div>
</td>
</tr>
<!-- SECTIONS -->
<template v-for="(section, index) in item.invoiceSections">
<tr
v-if="item.id && section.length > 0 && section.sectionDescription != ''"
:class="item.duplicate ? 'duplicateItemRow' : 'darkTableRow'"
:key="`${item.id}.${index}`"
>
<td class="text-left" colspan="2">{{ section.sectionDescription }}</td>
<td class="text-left" colspan="3">{{ section.repairReason }}</td>
<td class="text-right nowrap">{{ section.sectionTotal | currency }}</td>
<td>
<CommentsDialog
v-if="section.comments"
:itemId="section.id"
:comments="section.comments"
/>
</td>
</tr>
<!-- ITEMS -->
<tr
:class="[item.duplicate ? 'duplicateItemRow' : '', item.qtyRequested != item.qtyReceived ? 'qtyMismatchItemRow' : '' ]"
v-show="item.id"
v-for="(item, index) in section.invoiceItems"
:key="`${item.id}.${item.id}.${index}`"
>
<td class="text-left">{{ item.partNumber }}</td>
<td class="text-left">{{ item.partDescription }}</td>
<td class="text-right">
<v-tooltip v-if="item.qtyRequested != item.qtyReceived" bottom>
<template v-slot:activator="{ on }">
<span v-on="on" class="help">{{ item.qtyReceived }}</span>
</template>
<span>Requested {{ item.qtyRequested }}</span>
</v-tooltip>
<span v-else>{{ item.qtyReceived }}</span>
</td>
<td
:class="['text-right nowrap nopaddingRight', item.each > item.avgChargeAmount && item.avgChargeAmount != 0 ? 'over help' : '',
item.each < item.avgChargeAmount && item.avgChargeAmount != 0 ? 'under help' : '']"
>
<v-tooltip
v-if="item.each != item.avgChargeAmount && item.avgChargeAmount != 0"
bottom
>
<template v-slot:activator="{ on }">
<span v-on="on">{{ item.each | currency }}</span>
</template>
<span>Avg price: ${{ item.avgChargeAmount | currency }}</span>
</v-tooltip>
<span v-else>{{ item.each | currency }}</span>
</td>
<td
:class="['text-left nowrap lowpaddingLeft', item.each > item.avgChargeAmount && item.avgChargeAmount != 0 ? 'over' : '',
item.each < item.avgChargeAmount && item.avgChargeAmount != 0 ? 'under' : '']"
>
<v-tooltip v-if="item.nationalAccount" bottom>
<template v-slot:activator="{ on }">
<span v-on="on" class="help">N</span>
</template>
<span>
National
<br />Account
</span>
</v-tooltip>
</td>
<td class="text-right nowrap">{{ item.lineTotal | currency }}</td>
<td class="text-left">
<v-btn
outlined
small
color="primary"
dark
v-if="item.comments"
#click.stop="$set(dialogComments, item.id, true)"
>Comments</v-btn>
<CommentsDialog v-if="item.comments" :itemId="item.id" :comments="item.comments" />
</td>
</tr>
</template>
<tr key="row3" v-if="item.id">
<td colspan="13" class="divider"> </td>
</tr>
</transition-group>
</template>
You can use the transition group as your tbody, with the tag attribute like tag="tbody". So the transition group will display as a tbody instead of a span. The immediate children of the transition group also need unique keys and a v-if or v-show condition. The if or show condition also has to be triggered after whatever content you're looping through is ready, in order to se the transition. The click function will work inside of your table, but not on the tbody.
https://v2.vuejs.org/v2/guide/transitions.html#List-Transitions
<transition-group tag="tbody" name="invoice" v-for="(item, index) in items" :key="index">
<tr key="row1" v-if="showRow">
// Your columns
</tr>
<tr key="row2" v-if="showRow">
// Your columns
</tr>
</transition-group>
You can also use a tbody with the is attribute: is="transition-group", but I prefer the above option because the required name attribute of transition-group is not an allowed attribute of tbody. https://v2.vuejs.org/v2/api/#is
<tbody is="transition-group" name="invoice">
...
</tbody>

How to add custom class to td in slot items?

How to add class with condition to td ?
<template slot="items" slot-scope="props">
<td>
{{ props.item.id }}
</td>
<td :class="{'users-table__item--delete': props.item.login === 'admin'}">
{{ props.item.login }}
</td>
<td :class="{'users-table__item--delete': props.item.login === 'admin'}">
{{ props.item.type }}
</td>
</template>
It does not work.
Upd.
My version: "vuetify": "^2.1.15",
<template>
<v-card>
<v-card-text class="pa-0">
<template v-if="usersList.length">
<v-data-table
>
<template slot="items" slot-scope="props">
<td>
{{ props.item.id }}
</td>
<td>
{{ props.item.login }}
</td>
</template>
<template slot="item.action" slot-scope="props">
<div class="d-flex">
<v-btn
#click="changeUser(props.item.id)"
>Edit</v-btn>
</div>
</template>
</v-data-table>
</template>
<p v-else>No users</p>
</v-card-text>
</v-card>
</template>
I have item.action slot for show buttons. May be this affects on it ?
It seems you forgot to add tr tag.
This code works:
<template v-slot:item="props">
<tr>
<td :class="{'green': props.item.id > 4 && props.item.id <= 5,'red': props.item.id > 10}">{{ props.item.id }}</td>
</tr>
</template>
Result:

Get count of multiple nested items

I am displaying a Vuetify datatable and need to span some rows based on the count of nested data in the sample JSON below. The rowspan value should be a count of invoiceSections and invoiceItems.
I can get the count/length of invoiceSections but am having trouble with the count/length of invoiceItems.
[{
"vendorCode": "LOTMWI",
"vendorName": "LOVES TIRE CARE",
"orderId": "944803",
"invoiceSections": [{
"sectionDescription": "Lamps - Tail, Stop, Turn & License, Rear",
"repairReason": "INSPECTION",
"invoiceItems": [{
"partNumber": "",
"partDescription": "REPAIR LABOR",
"qtyReceived": 0,
},
{
"partNumber": "TL44032R",
"partDescription": "44032R S/T/T SUPER MODEL 44LED R",
"qtyReceived": 2,
},
{
"partNumber": "IMBRASS02",
"partDescription": "BRASS FITTINGS 02",
"qtyReceived": 1,
},
],
"id": 1,
}],
"id": 171,
}
]
A snipped of my code (trying to display the count to debug first):
<template v-slot:item="props">
<!-- <tbody> -->
<tr>
<td
:rowspan="rowspan"
valign="top"
class="pt-3 text-left font-weight-medium"
>{{ props.item.vendorCode }} {{ props.item.invoiceSections.length + 1}} - {{ props.item.invoiceSections.invoiceItems.length }}</td>
<td
:rowspan="rowspan"
valign="top"
class="pt-3 text-left font-weight-medium"
>{{ props.item.vendorName }}</td>
<td
:rowspan="rowspan"
valign="top"
class="pt-3 text-left font-weight-medium"
>{{ props.item.poNumber }}</td>
<td :rowspan="rowspan" valign="top" class="pt-3 text-left font-weight-medium">
<a
href="#"
data-tooltip="Click to view invoice"
data-tooltip-position="bottom"
target="_blank"
>{{ props.item.invoiceNumber }}</a>
</td>
<td
:rowspan="rowspan"
valign="top"
class="pt-3 text-left font-weight-medium"
>{{ props.item.invoiceDate | moment("M/D/YY") }}</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td
valign="top"
class="pt-3 text-right font-weight-medium nowrap"
>{{ props.item.invoiceAmount | currency }}</td>
<td :rowspan="rowspan" valign="top" class="pt-3 text-center">
<div v-if="props.item.approvalDate">
<v-icon color="green">mdi-check</v-icon>
</div>
<div v-else>
<v-btn #click="markApproved(props.item)" class="mx-2" fab small>
<v-icon color="grey lighten-2">mdi-check</v-icon>
</v-btn>
</div>
</td>
</tr>
<!-- SECTIONS -->
<template v-for="section in props.item.invoiceSections">
<tr class="darkTableRow" :key="section.id">
<td class="text-left" colspan="2">{{ section.sectionDescription }}</td>
<td class="text-left" colspan="3">{{ section.repairReason }}</td>
<td>$ 999</td>
<td>
<v-btn
outlined
small
color="primary"
dark
v-if="section.comments"
#click.stop="$set(dialogComments, section.id, true)"
>Comments</v-btn>
</td>
</tr>
<!-- ITEMS -->
<tr v-for="item in section.invoiceItems" :key="item.id">
<td class="text-left">{{ item.partNumber }}</td>
<td class="text-left">{{ item.partDescription }}</td>
<td class="text-right">{{ item.qtyReceived }}</td>
<td
class="text-right nowrap nopaddingRight"
:class="item.comparison"
>{{ item.each | currency }}</td>
<td class="text-left nowrap nopaddingLeft" :class="item.comparison">
<span
v-if="item.nationalAccount"
data-tooltip="National Account"
data-tooltip-position="right"
>N</span>
</td>
<td class="text-right nowrap">{{ item.secTotal | currency }}</td>
<td class="text-left">
<v-btn
outlined
small
color="primary"
dark
v-if="item.comments"
#click.stop="$set(dialogComments, item.id, true)"
>Comments</v-btn>
<v-dialog
v-model="dialogComments[item.id]"
scrollable
lazy
max-width="600"
:key="item.id"
>
<v-card>
<v-card-title class="headline grey lighten-2" primary-title>Comments</v-card-title>
<v-divider></v-divider>
<v-card-text v-html="item.comments" class="pt-4"></v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color="green darken-1"
outlined
#click.stop="$set(dialogComments, item.id, false)"
>Close</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</td>
</tr>
</template>
<tr>
<td colspan="13" class="divider"> </td>
</tr>
<!-- </tbody> -->
</template>

Vuetify editing v-data-table

Good day. i am trying to edit my v-data-table to suit my personal needs of rows and language. I managed to edit a few fields but i can't find a way to edit the "of" from "1-50 of 300".
I am reading the documentation but i got no idea on how to change that.
v-data-table
Here my current v-data-table documentation:
<v-data-table
:headers="headers"
:items="myItems"
:search="search"
no-results-text='LMAO NO DATA'
rows-per-page-text='Data'
:rows-per-page-items="[5, 50, 100, {text:'EVERYTHING', value: -1}] "
class="elevation-3"
>
EDIT: Sejir Ben Ali's answer is right but if you are having eslint errors showing up try using this:
<template v-slot:[`footer.page-text`]="props">
itens {{ props.pageStart }} - {{ props.pageStop }} of {{ props.itemsLength }}
</template>
From the docs (Slot: pageText - https://vuetifyjs.com/en/components/data-tables#slot-pagetext):
You can customize the page text displaying the range and total items by using the pageText slot.
<template>
<v-data-table
:headers="headers"
:items="desserts"
class="elevation-1"
>
<template v-slot:items="props">
<td>{{ props.item.name }}</td>
<td class="text-xs-right">{{ props.item.calories }}</td>
<td class="text-xs-right">{{ props.item.fat }}</td>
<td class="text-xs-right">{{ props.item.carbs }}</td>
<td class="text-xs-right">{{ props.item.protein }}</td>
<td class="text-xs-right">{{ props.item.iron }}</td>
</template>
<template v-slot:pageText="props">
ITEMS {{ props.pageStart }} - {{ props.pageStop }} OF {{ props.itemsLength }} // Edit this
// ^this is what you need
</template>
</v-data-table>
</template>

Vuetify table - custom css isn't applied to first row after page reload

Table:
<v-card :dark="true">
<v-card-title>
<v-btn color="indigo" dark #click="initialize"><v-icon dark>refresh</v-icon></v-btn>
<v-spacer></v-spacer>
<v-text-field append-icon="search" label="Search" single-line hide-details></v-text-field>
</v-card-title>
<v-data-table :dark="true" :headers="headers" :items="expenses" hide-actions class="elevation-1">
<template slot="headers" slot-scope="props">
<tr>
<th v-for="header in props.headers">{{header.text}}</th>
</tr>
</template>
<template slot="items" slot-scope="props">
<tr v-bind:class="getClass(props.item)">
<td class="text-xs-center">{{ props.item.year }}</td>
<td class="text-xs-center">{{ props.item.month }}</td>
<td class="text-xs-center">{{ props.item.day }}</td>
<td class="text-xs-center">{{ props.item.description }}</td>
<td class="text-xs-center">{{ props.item.value }}</td>
<td class="text-xs-center">{{ props.item.category }}</td>
<td class="text-xs-center">{{ props.item.details }}</td>
<td class="justify-center layout px-0">
<v-btn icon class="mx-0" #click="deleteItem(props.item)">
<v-icon color="pink">delete</v-icon>
</v-btn>
</td>
</tr>
</template>
<template slot="no-data">
<v-btn color="primary" #click="initialize">Reset</v-btn>
</template>
</v-data-table>
</v-card>
Css before page reload, after the code is edited in Webstorm and automatically compiled:
And after the reload:
And if I just remove the first row, the same happens no matter which one is first.
I had the same problem.
They problem here is that You override the items slot including <tr> tag. Without that everything will work fine. But for me, that was not a solution so if You want to override the <tr> tag also, add :key to it, like this: <tr :key="props.index">.
Take a look at source of v-data-table here.
To be honest, i don't know why it make that big difference but in my case that resolved the problem (i suspect that it is connected with vue list rendering).
Hope it helps!