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>
Related
with vuetify 2.x, I´d like to show my data grouped with option to collapse and expand on icon click.
My problem is if I use both 'group.header slot' and 'group slot', the first one doesn´t appear in the table.
If I hide 'group.header' slot I can put the group name into 'group slot', but I can´t use the icon click to collapse/expand the group.
<v-card-text>
<v-data-table
:headers="headers_parcelas"
:items="fitens_parcelas"
:items-per-page="5"
dense
hide-default-header
group-by="nome_conta"
show-group-by
class="elevation-1 marcarguias"
>
<template #[`group.header`]="{ group, isOpen, toggle }">
<th colspan="2">
<v-icon #click="toggle"
>{{ isOpen ? 'mdi-minus' : 'mdi-plus' }}
</v-icon>
{{ group }}
</th>
</template>
<template #group="props">
<!-- <span class="font-weight-bold">
{{ props.group }}
</span> -->
<v-data-table
:headers="props.headers"
:items="props.items"
dense
item-key="nome_conta"
hide-default-footer
>
<template #body="{ items }">
<tbody>
<tr v-for="item in items" :key="item.id">
<td>{{ item.descricao }}</td>
<td>{{ item.nome_forma_recebimento }}</td>
<td>{{ item.nome_quem_lancou }}</td>
<td>
{{ formatCurrency(item.valor_recebido) }}
</td>
<td class="text-center">
<span>
<v-icon
small
title="Apagar Movimento"
color="error"
#click.stop="deleteParcelaCaixa(item)"
>
mdi-delete
</v-icon>
</span>
</td>
</tr>
<tr>
<td colspan="5" class="text-center">
<span class="ml-10 font-weight-black">
SubTotal: {{ formatCurrency(itemTotal(items)) }}
</span>
</td>
</tr>
</tbody>
</template>
</v-data-table>
</template>
<template #[`body.append`]="{ items }">
<v-col
cols="12"
sm="6"
class="text-center font-weight-black text-h6 primary mx-auto"
>
Total:{{ formatCurrency(getTotalCost(items)) }}
</v-col>
</template>
</v-data-table>
</v-card-text>```
<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
I'm updating vuetify to the lastest version ( was 1.5 prior ) and I'm having trouble trying to adjust my table with the new props and slots. You can see my table right below, it has the possibility of selecting multiple lines and select all as well at the same time. I just need to purely replicate what I have, only to the new version and I don't know how to do it with the new slots.
<div class="col-12">
<v-data-table
v-model="selected"
:headers="headers"
:items="queriedData"
item-key="Id"
select-all
:pagination.sync="pagination"
:total-items="queriedData.lenght"
prev-icon="mdi-menu-left"
next-icon="mdi-menu-right"
sort-icon="mdi-menu-down"
>
<template v-slot:headers="props">
<tr>
<th v-if="canView">
<v-checkbox
:input-value="props.all"
:indeterminate="props.indeterminate"
primary
hide-details
color="white"
#click.stop="toggleAll"
class="table-checkbox-header"
></v-checkbox>
</th>
<th width="30px">
</th>
<th width="30px">
</th>
<th
v-for="header in props.headers"
:key="header.text"
:class="['column sortable', pagination.descending ? 'desc' : 'asc', header.value === pagination.sortBy ? 'active' : '']"
#click="changeSort(header.value)"
>
{{ header.text }}
<v-icon small>arrow_upward</v-icon>
</th>
</tr>
</template>
<template v-slot:no-data>
<div class="text-center">
{{NoInformation}}
</div>
</template>
<template v-slot:items="props">
<td v-if="canView">
<v-checkbox
v-model="props.selected"
primary
color="primary"
hide-details
class="table-checkbox-body"
></v-checkbox>
</td>
<td style="display: inline-flex;" >
<v-tooltip top color="primary" v-if="CanEdit">
<template v-slot:activator="{ on }">
<a v-on="on" class="btn-table-icon" #click.prevent="doSomething(props.item.Id)">
<i class="mdi mdi-eye icons-tables-margins"></i>
</a>
</template>
<span>{{view}}</span>
</v-tooltip>
<v-tooltip top color="primary" v-if="CanEdit" >
<template v-slot:activator="{ on }">
<a v-on="on" class="btn-table-icon" #click.prevent="doSomething(props.item.Id)">
<i class="mdi mdi-square-edit-outline icons-tables-margins"></i>
</a>
</template>
<span>{{view}}</span>
</v-tooltip>
</td>
<td>
<div v-if="props.item.Id!=0">
<span>Hello</span>
</div>
<div v-else>
<i class="mdi mdi-folder-lock-open"></i>
</div>
</td>
<td>{{ props.item.Name}}</td>
<td>{{ props.item.Name2}}</td>
<td>{{ props.item.Name3}}</td>
<td>{{ props.item.Name4}}</td>
<td :style="'color:' + props.item.ColorName" >{{ props.item.Name5}}</td>
</template>
</v-data-table>
</div>
Thank you.
Using slots seems not very different with the new version.
The only difference I can see is the props:
Before :
<template v-slot:headers="props">
Now :
<template v-slot:headers="{props}">
And for the checkboxes, you can just use the prop 'show-select' instead of using slots
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:
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>