How can I access index variable of a v-data-table? - vue.js

I normally would do this
<v-row v-for="(rule, index) in ruleDetails" :key="index">
... I should have access to index then...
... but now ...
I am not inside a v-for I am inside a table.
How can I access index variable of a table ?
<v-data-table
:headers="headers"
:items="rules"
:single-expand="singleExpand"
:expanded.sync="expanded"
item-key="name"
show-expand
class="elevation-0"
>
<template v-slot:top>
<v-toolbar flat>
<v-toolbar-title>{{ name }}</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn outlined class="green--text" #click="showAddRuleModal()">
<v-icon dark> add </v-icon>
Rule
</v-btn>
</v-toolbar>
</template>
<template v-slot:expanded-item="{ headers, item }">
<td :colspan="headers.length">{{ item.conditions }}</td>
</template>
<template v-slot:item.id="{ item }">
<v-btn outlined class="orange--text" #click="showEditRuleModal(index)"> Edit </v-btn>
<v-btn outlined class="red--text" #click="showDeleteRuleModal(index)"> Delete </v-btn>
</template>
</v-data-table>

You could get it using the item slot as the second argument:
<template v-slot:item="{ expand, index, item }">
<v-btn outlined class="orange--text" #click="showEditRuleModal(index)"> Edit </v-btn>
<v-btn outlined class="red--text" #click="showDeleteRuleModal(index)"> Delete </v-btn>
</template>

based on the documentation you have access to index if you use item slot: item-slot documentation
but if you don't want to use item slot, you can use a computed to include the index in the objects that you are passing to the v-data-table and that computed is like this:
computed: {
dataTableItems() {
return this.rules.map((x, i) => ({index: i, ...x}));
}
}
then in each slot where you have access to item you can find the index by using item.index

Related

Vuetify access v-slot:item in datatable custom component

I've made a custom datatable component, where my Table.vue file is shown below:
<template>
<div>
<v-data-table
:headers="headers"
:items="items"
:search="search"
:loading="loading"
loading-text="Loading... Please wait"
dense
>
<template v-slot:top>
<v-toolbar dark flat dense>
<v-toolbar-title>{{ title }}</v-toolbar-title>
<v-spacer></v-spacer>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="Search"
single-line
hide-details
></v-text-field>
<v-spacer></v-spacer>
</v-toolbar>
</template>
</v-data-table>
</div>
</template>
<script>
export default {
name: "Table",
props: [
"headers",
"items",
"title",
"itemsPerPage",
"loading",
],
data: function () {
return {
search: '',
}
},
methods: {
},
};
</script>
And I'm using it like that:
<Table
:headers="headers"
:items="groups"
:loading="loading"
title="Baseny"
>
</Table>
Everything is fine, but I want to add custom columns with actions for every input (every input has different ID)
Normally (without a custom component) I'd use the following code:
<v-data-table
:headers="headers"
:items="times"
:items-per-page="5"
:search="search"
:loading="loading"
loading-text="Ładowanie... Proszę czekać"
>
<template v-slot:top>
<v-toolbar dark flat dense>
<v-toolbar-title>Lista zajęć</v-toolbar-title>
<v-spacer></v-spacer>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="Szukaj"
single-line
hide-details
></v-text-field>
<v-spacer></v-spacer>
<v-btn
color="primary"
:to="{ name: 'admin.times.create' }"
>
Dodaj zajęcie
</v-btn>
</v-toolbar>
</template>
<template v-slot:item.actions="{ item }">
<v-icon
small
class="mr-2"
#click="show(item)"
>
mdi-pool
</v-icon>
<v-icon
small
class="mr-2"
#click="edit(item)"
>
mdi-pencil
</v-icon>
</template>
</v-data-table>
I'm using this v-slot:
<template v-slot:item.actions="{ item }">
<v-icon
small
class="mr-2"
#click="show(item)"
>
mdi-pool
</v-icon>
<v-icon
small
class="mr-2"
#click="edit(item)"
>
mdi-pencil
</v-icon>
</template>
However, when I wrote the custom reusable table components it didn't work when I put these lines into tag.
How can I use my custom components properly in this scenario?
What you want to achieve is I believe a wrapper component. You want to wrap a component on top of another one to let him have some custom properties that you want to reuse in your application.
What you need is a small snippet that will allow your slots to be used:
<!-- pass through scoped slots -->
<template v-for="(_, scopedSlotName) in $scopedSlots" v-slot:[scopedSlotName]="slotData">
<slot :name="scopedSlotName" v-bind="slotData" />
</template>
<!-- pass through normal slots -->
<template v-for="(_, slotName) in $slots" v-slot:[slotName]>
<slot :name="slotName" />
</template>
You can find the source of this here
Basically, here how you can rewrite your CustomTable.vue:
<template>
<div>
<v-data-table
v-bind="$attrs"
v-on="$listeners"
:search="search"
loading-text="Loading... Please wait"
dense
>
<!-- pass through scoped slots -->
<template
v-for="(_, scopedSlotName) in $scopedSlots"
v-slot:[scopedSlotName]="slotData"
>
<slot :name="scopedSlotName" v-bind="slotData" />
</template>
<!-- pass through normal slots -->
<template v-for="(_, slotName) in $slots" v-slot:[slotName]>
<slot :name="slotName" />
</template>
<template v-slot:top>
<v-toolbar dark flat dense>
<v-toolbar-title>{{ title }}</v-toolbar-title>
<v-spacer></v-spacer>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="Search"
single-line
hide-details
></v-text-field>
<v-spacer></v-spacer>
</v-toolbar>
</template>
</v-data-table>
</div>
</template>
<script>
export default {
name: "CustomTable",
props: ["title"],
data: function () {
return {
search: "",
};
},
methods: {},
};
</script>
I made a codesandbox to show you how it works:
https://codesandbox.io/s/vuetify-2-forked-3lp9y?file=/src/components/CustomTable.vue
I also added automatic attribute and listeners bindings on your table, to allow you to use all the features that Vuetify provides.

how to access parent v-data-table from template in nested v-data-table?

I have a page made with vue using vuetify. It displays a v-data-table which can be expanded to reveal another v-data-table, like this:
<div class="subsection">
<v-data-table
:headers="prescriptionHeaders"
:items="pendingItems"
show-expand
item-key="id"
>
<template v-slot:expanded-item="{headers,item}">
<td :colspan="headers.length">
<v-data-table
:headers="pendingPrestationHeaders"
:items="item.prestations"
v-model="selected"
>
<template v-slot:[`item.actions`]="{ item }">
<div class="table-row-actions">
<v-tooltip left v-if="item.categeoryTypeId === 6">
<template v-slot:activator="{ on, attrs }">
<v-icon
v-bind="attrs"
v-on="on"
#click="func1(item)"
class="action-doc"
>
mdi-file-document-outline
</v-icon>
</template>
<span>blablabla</span>
</v-tooltip>
</div>
</template>
</v-data-table>
</td>
</template>
</v-data-table>
</div>
the problem is that I need to call func1 with a property of the item from the outer v-data-table. How can I access it from within my <template v-slot:[`item.actions`] template ? I know I could include a reference to the parent item in my child item, or just duplicate the data that I need from the parent into the child (that's what I'm currently doing), but I was just curious to find out whether there is a way to refer to the "outer" item in the template slot, but I guess not.
To access the item of the outer v-data-table, you need to change the inner data table with props.item
Something like this should work.
<template v-slot:[`props.item.actions`]="{ props.item }">
<div class="table-row-actions">
<v-tooltip left v-if="props.item.categeoryTypeId === 6">
<template v-slot:activator="{ on, attrs }">
<v-icon
v-bind="attrs"
v-on="on"
#click="func1(item)"
class="action-doc"
>
mdi-file-document-outline
</v-icon>
</template>
<span>blablabla</span>
</v-tooltip>
</div>
</template>
I've exactly the same challenge. I want to access to item ID of the outer v-data-table from the inner one. The paragraph shows the data correctly. How to access this value from the inner data-table?
<template v-slot:expanded-item="{ item }">
<td :colspan="attachmentHeaders.length">
<p>{{item.finDocId}}</p>
<v-data-table
:headers="attachmentHeaders"
:items="item.attachmentPlainDtos"
item-key="finDocId"
disable-pagination
:hide-default-footer="true"
no-data-text='Geen bijlagen'
>
<template v-slot:[`item.attachmentActions`]="{ item }">
<v-icon large #click="removeAttachment(item.id, item.attachmentId)">
mdi-delete
</v-icon>
</template>
</v-data-table>
</td>
</template>

Vuetify - v-data-table break the height of a v-card

I use Vuetify for development a dashboard. I need all cards whit a specific height, in this case 450px. I fill the v-data-table from axios petition, when the v-data-table fill whit data, it break the height of v-card.
See the v-card Tabla Tarificador carteras
this is my v-card component code:
<v-row>
<v-col cols="12" md="7">
<v-card height="450">
<v-card-text>
<v-row>
<v-col cols="12">
<v-data-table
:fixed-header="true"
:headers="tablas.tarificadorCarteras.cabeceras"
:items="tablas.tarificadorCarteras.datos"
>
<template v-slot:[`item.cartera`]="{ item }">
<a #click="obtenerGraficoTarificadorCartera(item.cartera)" class="text-capitalize">{{ item.cartera }}</a>
</template>
<template v-slot:[`item.minutos_entel`]="{ item }">
{{ new Intl.NumberFormat('es-CL').format(item.minutos_entel) }}
</template>
<template v-slot:[`item.minutos_movistar`]="{ item }">
{{ new Intl.NumberFormat('es-CL').format(item.minutos_movistar) }}
</template>
<template v-slot:[`item.minutos_pacifico`]="{ item }">
{{ new Intl.NumberFormat('es-CL').format(item.minutos_pacifico) }}
</template>
</v-data-table>
</v-col>
</v-row>
</v-card-text>
</v-card>
</v-col>
</v-row>
I need see the v-data-table inside the v-card scrollable and responsive. I try to make it, but can't
Set max-height="450" style="overflow-y:auto" to v-col

Switching child components inside v-data-table using buttons(filters)

I have a parent component with 2 buttons that I want to use as filters. the child component is called inside a v-data-table.
ScanGrid(parent):
<template>
<v-card class="ma-5" v-else>
<v-flex row xs12 class="pa-3 ml-3">
<div class="mr-3 mt-2">
<h3>Regrouper par :</h3>
</div>
<div>
<v-btn-toggle color="success" v-model="groupBy">
<v-btn
text
value="barCode"
class="lowerCase"
v-ripple="{ class: 'success--text' }"
>
Code barre
</v-btn>
<v-btn
text
value="productDef"
class="lowerCase"
v-ripple="{ class: 'success--text' }"
>
Définition de produit
</v-btn>
</v-btn-toggle>
</div>
</v-flex>
<v-card-text>
<v-layout align-center>
<v-data-table
:headers="headers"
:items="items"
item-key="StorageName"
show-expand
single-expand
:expanded="expanded"
hide-default-footer
#click:row="clickedRow"
>
<template v-slot:expanded-item="{ item }">
<td :colspan="12">
<ScanGridCode :item="item"></ScanGridCode>
</td>
</template>
<template v-slot:expanded-item="{ item }">
<td :colspan="12">
<ScanGridDef :item="item"></ScanGridDef>
</td>
</template>
</v-data-table>
</v-layout>
</v-card-text>
</v-card>
</template>
The children components I want to switch using the buttons are called ScanGridCode and ScanGridDef inside templates in v-data-table. I tried to find examples of filtering buttons online but couldn't find anything like what I want to do.
I'm using Vue 2.6.10 with Vuetify 2.1.7
You're almost done already, since you already have the v-btn-toggle set up with v-model="groupBy". All you still need is to add v-if to each template, like:
<template v-if="groupBy=='barCode'" v-slot:expanded-item="{ item }">

How to add right click event for v-treeview to open menu in vuetify?

I want to add right click event for v-treeview to open menu but I fail. I created a version that can open menu when left click and the main code is
<v-treeview v-model="tree" :open="open" :items="items" activatable item-key="name" >
<template v-slot:label="{item, open, selected}">
<v-menu
:value="showMenu"
>
<template v-slot:activator="{ on }">
<v-btn
flat
:ripple="false"
class="ma-0 pa-0"
v-on="on"
>
<!--button icon-->
<v-icon v-if="!item.file">
{{ open ? 'mdi-folder-open' : 'mdi-folder' }}
</v-icon>
<v-icon v-else>
{{ files[item.file] }}
</v-icon>
<!--button text-->
{{item.name}}
</v-btn>
</template>
<v-list>
<v-list-tile v-for="menuItem in menuItems" :key="menuItem">
<v-list-tile-title>{{menuItem}}</v-list-tile-title>
</v-list-tile>
</v-list>
</v-menu>
</template>
</v-treeview>
Note: the source code can be run at https://codepen.io/lhuangjs/pen/axMpYJ
And I am confused with v-on="on" in activator slot so much and I get some info from https://github.com/vuetifyjs/vuetify/issues/6866. However I still cannot understand it. Is there any more clear explanation?
thanks!
You have to use #contextmenu on the tree nodes.
I have tried to achieve what you tried. https://codepen.io/anon/pen/QPoYOQ?editors=1010#anon-login
But to make the tree look good, you have to do some CSS adjustments. I'm not sure any element other than v-btn or v-card accepts the #contextmenu event handler.
<div id="app">
<v-app id="inspire">
<v-treeview v-model="tree" :open="open" :items="items" activatable item-key="name">
<template v-slot:label="{item, open, selected}">
<v-btn flat #contextmenu="show">
<!--button icon-->
<v-icon v-if="!item.file">
{{ open ? 'mdi-folder-open' : 'mdi-folder' }}
</v-icon>
<v-icon v-else>
{{ files[item.file] }}
</v-icon>
<!--button text-->
{{item.name}}
</v-btn>
</template>
</v-treeview>
<v-menu v-model="showMenu" :position-x="x" :position-y="y" absolute offset-y>
<v-list>
<v-list-tile v-for="menuItem in menuItems" :key="menuItem" #click="clickAction">
<v-list-tile-title>{{menuItem}}</v-list-tile-title>
</v-list-tile>
</v-list>
</v-menu>
</v-app>
</div>
you can popup a context menu by adding #contextmenu event in a label slot:
<v-treeview :items="files" dense open-on-click transition hoverable>
<template v-slot:prepend="{ item, open }">
<v-icon v-if="item.children">{{ open ? 'mdi-folder-open' : 'mdi-folder' }}</v-icon>
<v-icon v-else>{{ icon(item.basename) }}</v-icon>
</template>
<template v-slot:label="{ item }">
<div #contextmenu.prevent="right($event, item)">{{ item.basename }}</div>
</template>
</v-treeview>