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
Related
In Vuetify, if I want to move my delete button closer to the expand button.
How do I do that ?
<v-expansion-panel-header>
{{ vehicle.VIN }}
<v-icon v-if="type == 'saved'" color="teal"> mdi-check </v-icon>
<v-btn
text
v-if="type == 'saved'"
color="red"
#click="remove(index, type)"
>
DELETE
</v-btn>
</v-expansion-panel-header>
You can reset the flex-grow property on the delete button using a Vuetify helper class.
class="flex-grow-0"
Snippet:
<div id="app">
<v-app>
<v-expansion-panel-header>
{{ vehicle.VIN }}
<v-icon v-if="type == 'saved'" color="teal"> mdi-check </v-icon>
<v-btn
class="flex-grow-0"
text
v-if="type == 'saved'"
color="red"
#click="remove(index, type)"
>
DELETE
</v-btn>
</v-expansion-panel-header>
</v-app>
</div>
Vuetify Docs: https://vuetifyjs.com/en/styles/flex/#flex-grow-and-shrink
It can also be achieved using row and column-
<v-expansion-panel-header>
<v-row no-gutters>
<v-col>
hello
</v-col>
<v-col>
<v-icon color="teal"> mdi-check </v-icon>
</v-col>
<v-col align="right">
<v-btn text color="red">
DELETE
</v-btn>
</v-col>
</v-row>
</v-expansion-panel-header>
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.
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
I would like to align all seven of those buttons centered. As you can see the last one is a bit off compared to the first one.
How do I achieve this?
I've already tried justify="center" and justify="space-around"
Here is my code:
<v-row no-gutters justify="space-around">
<v-col v-for="(item, index) in buttons" :key="index">
<toggle-button
:weekday="item.weekday"
:button="item.state"
></toggle-button>
</v-col>
</v-row>
Here is the toggle-button component:
<template>
<v-btn
outlined
depressed
:class="button ? 'primary white--text' : 'outlined'"
#click="button ? (button = false) : (button = true)"
v-model="button"
icon
>
{{ $t("roomservice.weekdays." + weekday) }}
</v-btn>
</template>
<script>
export default {
data() {
return {};
},
props: ["button", "weekday"]
};
</script>
v-col is not a flex and contents inside (toggle-button) are justified to start form left.
you can fix this by adding class="d-flex justify-center" on v-col
<v-row no-gutters justify="space-around">
<v-col
class="d-flex justify-center"
v-for="(item, index) in buttons"
:key="index">
<toggle-button
:weekday="item.weekday"
:button="item.state"
></toggle-button>
</v-col>
</v-row>
My vue component like this :
<template>
<v-row>
<v-col cols="12" sm="6" md="4">
<v-dialog
v-for="(item, i) in test" :key="i"
ref="dialog"
v-model="modal"
:return-value.sync="item.date"
persistent
width="290px"
>
<template v-slot:activator="{ on }">
<v-text-field
v-model="item.date"
label="Picker in dialog"
prepend-icon="event"
readonly
v-on="on"
></v-text-field>
</template>
<v-date-picker v-model="date" scrollable>
<div class="flex-grow-1"></div>
<v-btn text color="primary" #click="modal = false">Cancel</v-btn>
<v-btn text color="primary" #click="$refs.dialog.save(item.date)">OK</v-btn>
</v-date-picker>
</v-dialog>
</v-col>
</v-row>
</template>
<script>
export default {
data: () => ({
test: [
{ id: 1, date: new Date().toISOString().substr(0, 10) },
{ id: 2, date: new Date().toISOString().substr(0, 10) },
],
modal: false,
}),
}
</script>
multiple datetimepicker doesn't work properly
if i click ok button in the modal, there exist error like this :
[Vue warn]: Error in v-on handler: "TypeError: _vm.$refs.dialog.save is not a function"
How can I solve this problem?
First off, you need to return the whole object from the dialog, not just the date. With :return-value.sync="item.date", the objects in test will have only date as their only property. Your date picker also has a wrong binding.
<v-dialog
v-for="(item, i) in test" :key="i"
ref="dialog"
v-model="modal"
:return-value.sync="item"
persistent
width="290px"
>
<template v-slot:activator="{ on }">
<v-text-field
v-model="item.date"
label="Picker in dialog"
prepend-icon="event"
readonly
v-on="on"
></v-text-field>
</template>
<v-date-picker v-model="item.date" scrollable>
<div class="flex-grow-1"></div>
<v-btn text color="primary" #click="modal = false">OK</v-btn>
</v-date-picker>
</v-dialog>