Vuetify access v-slot:item in datatable custom component - vue.js

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.

Related

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

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

Vuetify v-slot:activator not stopping

I have a text-field that's triggering a color-picker. This is inside a v-for loop as well. All is fine until the activator triggers the color-picker and multiple pickers are triggered with a mashup of the v-for data.
You can see the mashup of data at the top, as well as mutliple color pickers activated.
Any idea why? My code is below:
<v-tab-item>
<v-card
flat
v-for="(colorMenu, index) in colorMenus"
:key="index"
>
<v-card-text>
<v-row
justify="start"
align="center">
<v-col
cols="4"
>
<p class="font-weight-bold text-subtitle-2 mt-4">{{ colorMenu.title }}</p>
</v-col>
<v-col
cols="8"
>
<v-text-field
v-model="myModels[colorMenu.type]"
v-mask="mask"
hide-details
class=""
solo
>
<template
v-slot:append
>
<v-menu
v-model="menu"
top
nudge-bottom="105"
nudge-left="16"
:close-on-content-click="false"
>
<template
v-slot:activator="{ on }"
>
<div
:style="{ backgroundColor: selectColors[index], borderRadius: menu ? '50%' : '4px'}"
v-on="on"
class="color_select"
/>
</template>
<v-color-picker
v-model="selectColors[index]"
flat
>
</v-color-picker>
</v-menu>
</template>
</v-text-field>
</v-col>
</v-row>
<v-divider class="mt-3"></v-divider>
</v-card-text>
</v-card>
</v-tab-item>
The main problem is all the v-menu's are bound to the single menu Boolean, causing all the menus to open and close at the same time. To resolve this, make menu an array of Booleans (like you've done with the other props within the v-for).
Another issue is your backgroundColor is bound to selectColors[index], but that's an object from the v-color-picker. The object has a hex property that contains the hex string of the color, which would be appropriate for the backgroundColor value.
<v-menu 👇
v-model="menu[index]"
top
nudge-bottom="105"
nudge-left="16"
:close-on-content-click="false"
>
<template v-slot:activator="{ on }"> 👇 👇
<div :style="{ backgroundColor: selectColors[index]?.hex, borderRadius: menu[index] ? '50%' : '4px'}"
v-on="on"
class="color_select"
/>
</template>
<v-color-picker v-model="selectColors[index]" flat>
</v-color-picker>
</v-menu>
export default {
data() {
return { 👇
menus: [],
⋮
}
}
}
demo

How can I call the edit component within another file?

I have a project and this project is for the owners of the purchase for the purchase of cars and many other operations, but I have a table with several columns, and within these columns there is a column I listen to action and this column contains a button called Edit and I want when I click on the Edit button to be used The component of the modification within this file, how can I do this?
And it is the Edit file in which the Edit form is located.
Edit.vue:
<template>
<v-row justify="center">
<v-dialog v-model="editDialog" persistent max-width="1050px" height="400px">
<template v-slot:activator="{ on, attrs }">
<v-btn
fab
accent
class="grey lighten-1 margin pa-4"
dark
v-bind="attrs"
v-on="on"
>
<v-icon>
mdi-pencil
</v-icon>
</v-btn>
</template>
<v-card>
<v-layout>
<v-flex xs12>
<div class="myfont pl-5">
<v-card-title>
<span> Edit Car</span>
</v-card-title>
</div>
</v-flex>
</v-layout>
<v-divider xs12></v-divider>
<v-layout>
<v-flex xs12>
<v-card-text>
<v-container>
<v-row>
<v-col cols="12">
<v-text-field
name="name"
label="Name"
id="name"
class="colorLabel"
v-model="editedName"
multi-line
required
></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field
name="priceOfSale"
label="Price Of Sale"
id="priceOfSale"
v-model="editedPrice"
class="colorLabel"
multi-line
required
></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field
name="numberOfSeats"
label="NumberOfSeats"
id="numberOfSeats"
v-model="editedNumberOfSeats"
multi-line
required
></v-text-field>
</v-col>
</v-row>
</v-container>
</v-card-text>
</v-flex>
</v-layout>
<v-divider></v-divider>
<v-layout>
<v-flex xs12>
<v-card-actions>
<v-btn class="myfont pl-5 text-right" text #click="onSaveChanges">
Save
</v-btn>
<v-btn
class="myfont pl-5 text-center"
text
#click="editDialog = false"
>
Cancel
</v-btn>
</v-card-actions>
</v-flex>
</v-layout>
</v-card>
</v-dialog>
</v-row>
</template>
<script>
import { mapActions } from "vuex";
import ActionsTypes from "../store/types/actions-types";
export default {
props: ["car"],
data() {
return {
editedName: this.car.name,
editedPrice: this.car.price,
editedNumberOfSeats: this.car.seatsNumber,
};
},
methods: {
...mapActions({
editCarInformations: ActionsTypes.EDIT_CAR_ACTION,
}),
onSaveChanges() {
const UpdatedCar = { ...this.car };
UpdatedCar.name = this.editedName;
UpdatedCar.price = this.editedPrice;
UpdatedCar.seatsNumber = this.editedNumberOfSeats;
this.editCarInformations(UpdatedCar);
},
},
};
</script>
This file, in which there is a table containing several columns, and the last column is Action, which contains the Modify button, the Modify button, and when I press it, the universe of the amendment is called.
viewAllCars:
<template>
<v-app class="bg">
<v-container>
<v-card
class="mx-auto mt-5 pa-3"
max-width="100%"
id="limited-products"
:style="'border: 0px solid #D50000;'"
>
<v-btn class="red accent-4 color myfont pl-3" #click="onCreateCar">
evict Cashig
</v-btn>
<v-data-table
:headers="tableHeaders"
:items="loadedCarsGetter"
:page.sync="page"
:items-per-page="itemsPerPage"
hide-default-footer
class="elevation-1"
#page-count="pageCount = $event"
>
<template #[`item.actions`]="{ item }">
<v-btn icon #click="edit(item.id)">
<v-icon>mdi-pencil</v-icon>
</v-btn>
<v-btn icon #click="delete (item.id)">
<v-icon>mdi-delete</v-icon>
</v-btn>
</template>
</v-data-table>
<!-- pagination -->
<div class="text-center pt-2">
<v-pagination v-model="page" :length="pageCount"></v-pagination>
<v-text-field
:value="itemsPerPage"
label="Items per page"
type="number"
min="-1"
max="15"
#input="itemsPerPage = parseInt($event, 10)"
class="pl-7 pr-7"
></v-text-field>
</div>
</v-card>
</v-container>
</v-app>
</template>
<script>
import { mapActions, mapGetters } from "vuex";
import ActionsTypes from "../../store/types/actions-types";
import GettersTypes from "../../store/types/getters-types";
export default {
data() {
return {
page: 1,
pageCount: 0,
itemsPerPage: 10
};
},
created() {},
computed: {
...mapGetters({
loadedCarsGetter: GettersTypes.GET_CAR_FORM_GETTER,
tableHeaders: GettersTypes.GET_HEADERS_TABLE_GETTER,
}),
},
mounted() {
// this.loadedCarsGetter();
this.loadedCarsAction();
},
methods: {
...mapActions({
editcardispatcher: ActionsTypes.EDIT_CAR_ACTION,
deletecardispatcher: ActionsTypes.DELETE_CAR_ACTION,
loadedCarsAction: ActionsTypes.GET_ALL_CAR_ACTION
}),
edit() {
this.editcardispatcher({});
},
delete(){
this.deletecardispatcher(
this.car.id
)
}
},
};
</script>
First of all, you don't need the "v-row" in the Edit.vue. Remove it.
As you have the button as the activator, you should just add the component as Avraham mentioned. But you need to know that there are some caveats with this approach
This is gonna be increasing the memory usage by the browser. As a separate instance of Edit.vue will be added to the DOM for each row in your table.
Each Edit.vue instance will preserve the data in it with the changes that the user might make. And you'll have to handle the data resets.
A better solution would be to add only one instance of Edit.vue and add/remove the component from the DOM using a v-if.
This will keep your table using one instance of Edit.vue, and the addition and removal of the component will handle the data reset.
Something like this
In the file that contains the v-data-table, update the template as follows
<template>
......
<v-data-table ...>
...
<template #[`item.actions`]="{ item }">
<v-btn icon #click="edit(item.id)">
<v-icon>mdi-pencil</v-icon>
</v-btn>
<v-btn icon #click="delete(item.id)">
<v-icon>mdi-delete</v-icon>
</v-btn>
</template>
...
</v-data-table>
<edit :car="item" v-if="showEditDialog = true" #closed="showEditDialog = false" />
......
</template>
<script>
import Edit from 'Edit.vue'
export default {
components: { Edit },
data: () =({
item: {},
showEditDialog: false,
}),
methods: {
edit(item) {
this.item = item
this.showEditDialog = true
}
}
}
</script>
In your Edit.vue, add a watcher for the "editDialog" property to emit an event to remove the edit dialog from the DOM.
watch: {
editDialog(val){
if(!val)
this.$emit('closed')
}
}
And remove this part from the Edit.Vue
<template v-slot:activator="{ on, attrs }">
<v-btn
fab
accent
class="grey lighten-1 margin pa-4"
dark
v-bind="attrs"
v-on="on"
>
<v-icon>
mdi-pencil
</v-icon>
</v-btn>
</template>
Good luck.
You should import the Edit.vue component in the car viewer component and use it instead of the edit button:
...
<template #[`item.actions`]="{ item }">
<!-- Pass the item to the `car` prop -->
<edit :car="item" />
<v-btn icon #click="delete (item.id)">
<v-icon>mdi-delete</v-icon>
</v-btn>
</template>
...
<script>
import Edit from 'Edit.vue' // make sure the path to the component is correct
export default {
components: { Edit },
...
};
</script>

Vue components: Using slots within slots

I have v-data-table as its own component to make it more generic for my components. I would like to have the option to provide that component how each item in the table is formatted. That would be done with a slot. But the problem is, that the provided slot has a slot for v-data-table. See the example, this is difficult to explain.
In my DataTable component I have:
<v-data-table
:headers="tableHeaders"
:items="items"
:item-key="itemKey"
:loading="isLoading"
v-model="selectedRows"
show-select
>
<!-- What do i put here? -->
</v-data-table>
And I use that data table from my other component like this:
<data-table
:table-headers="tableHeaders"
:items="messages"
:is-loading="isLoading"
:selected-rows="selectedRows"
:item-key="'MessageID'"
>
<template v-slot:item.actions="{ item }">
<v-btn :to="`messages/${item.MessageID}`" icon>
<v-icon>mdi-chevron-right</v-icon>
</v-btn>
</template>
<template v-slot:item.CreatedDateTime="{ item }">
<span>{{ format(new Date(item.CreatedDateTime)) }}</span>
</template>
</data-table>
I need the result to be rendered as:
<v-data-table
:headers="tableHeaders"
:items="items"
:item-key="itemKey"
:loading="isLoading"
v-model="selectedRows"
show-select
>
<template v-slot:item.actions="{ item }">
<v-btn :to="`messages/${item.MessageID}`" icon>
<v-icon>mdi-chevron-right</v-icon>
</v-btn>
</template>
<template v-slot:item.CreatedDateTime="{ item }">
<span>{{ format(new Date(item.CreatedDateTime)) }}</span>
</template>
</v-data-table>
So the problem is, how do I provide the templates within data-table tag to be rendered within v-data-table tag?
I had the exact same issue. I found a solution utilizing the $scopedSlotes object, like this:
<v-data-table
:headers="tableHeaders"
:items="items"
:item-key="itemKey"
:loading="isLoading"
v-model="selectedRows"
show-select
>
<template
v-for="slot in Object.keys($scopedSlots)"
:slot="slot"
slot-scope="scope"
>
<slot
:name="slot"
v-bind="scope"
></slot>
</template>
</v-data-table>
You will have to re-expose the slots in your "wrapper" component:
<v-data-table
:headers="tableHeaders"
:items="items"
:item-key="itemKey"
:loading="isLoading"
v-model="selectedRows"
show-select
>
<template v-slot:body="bodyParams">
<slot name="body" v-bind="bodyParams" />
</template>
<template v-slot:expanded-item="expandedParams">
<slot name="expanded-item" v-bind="expandedParams" />
</template>
<template v-slot:header="headerParams">
<slot name="header" v-bind="headerParams" />
</template>
<template v-slot:footer="footerParams">
<slot name="footer" v-bind="footerParams" />
</template>
<template v-slot:item="itemParams">
<slot name="item" v-bind="itemParams" />
</template>
<template v-slot:progress="progressParams">
<slot name="progress" v-bind="progressParams" />
</template>
<!-- put here the other slots you are interested in -->
</v-data-table>

VueJs Vuetify Autocomplete with links

I'am using vuetify and i want make auto complete like the one on their official site.
But i'm faces some issues :
The items value not appear in the return list and i don't know how to make appear the links.
Here is my code.
Thanks You
<template>
<v-app>
<v-app-bar app color="primary" dark>
<v-app-bar-nav-icon #click="drawer = true"></v-app-bar-nav-icon>
<v-toolbar-title> Board </v-toolbar-title>
<v-spacer></v-spacer>
<v-autocomplete
:loading="loading"
:filter="v => v"
:items="items"
:search-input.sync="search"
v-model="select"
flat hide-no-data hide-details return-object placeholder="Search ...">
<v-list-tile
slot="prepend-item"
class="grey--text">
{{ items.length }} menus found
</v-list-tile>
<template slot="selection" slot-scope="{ item }">
{{item.name}} {{item.url}}
</template>
<template slot="item" slot-scope="{ item }">
<v-list-tile-content>
<p class='fullName'>{{item.name}} {{item.url}}</p>
</v-list-tile-content>
</template>
</v-autocomplete>
</v-app-bar>
<v-main>
<HelloWorld/>
</v-main>
</v-app>
I went through your issue and try to make a codepen and it worked.
My suggestion is that you should consider data structure when you use object with auto complete and it 's needed even you use v-select.
Please check this pen. https://codepen.io/endmaster0809/pen/qBZRywZ
items: [
{
name: 'Alerts',
url: 'https://vuetifyjs.com/en/components/alerts/'
},
{
name: 'Autocompletes',
url: 'https://vuetifyjs.com/en/components/autocompletes/#autocompletes'
}
],
select: {
name: 'Alerts',
url: 'https://vuetifyjs.com/en/components/alerts/'
},