I am looking at the vuetify table example for CRUD operations. A codepen for it is here:
https://codepen.io/uglyhobbitfeet/pen/oNvKaaL
I added a parent form that wraps around the table and another component. It validates that the table has more than 0 rows and validates the other component before the user can click the 'continue' button.
In the provided codepen, when the user clicks on the 'New Item' button (located at the top-right of the table), I would like to validate the popped up fields before the user can 'save' the data to the table. How would that be done? I have tried to wrap the v-dialog in a separate v-form from the parent form, but I couldn't get it to work and I'm not sure nesting forms is the way to go. Any suggestions?
Since SO requires code to be posted when providing a codepen link here's a small snippet.
<v-data-table
:headers="headers"
:items="desserts"
sort-by="calories"
class="elevation-1"
>
reassignFormInputs(form) {
const inputs = [];
// Copied from VForm's previous life* which had a getInputs() function
const search = (children, depth = 0) => {
for (let index = 0; index < children.length; index++) {
const child = children[index];
if (child.errorBucket !== undefined) inputs.push(child);
else search(child.$children, depth + 1);
}
if (depth === 0) return inputs;
};
search(form.$children);
form.inputs = inputs;
},
saveForm() {
this.reassignFormInputs(this.$refs.form);
if (this.valid) {
console.log(this.lessonPlanData);
} else {
this.$refs.form.validate();
}
},
Assign your child components to the parent input.
Source: https://github.com/vuetifyjs/vuetify/issues/4900#issuecomment-423600028
I get your code and make some changes, I added the v-form inside de v-container to validate, then I created the Rules for the inputs to validate, and disabled the button if the form is not valid for both editing and new items.
In the second part the script I added the rules to validate the fields and then in the save function I check if everything is ok.
In the other form (father form) you can just verify if some item was added since you already validated the data inside the form in the dialog.
this.desserts.length > 0 //And then proceed
For more info see the docs
Template
<div id="app">
<v-app id="inspire">
<v-data-table
:headers="headers"
:items="desserts"
sort-by="calories"
class="elevation-1"
>
<template v-slot:top>
<v-toolbar flat color="white">
<v-toolbar-title>My CRUD</v-toolbar-title>
<v-divider
class="mx-4"
inset
vertical
></v-divider>
<div class="flex-grow-1"></div>
<v-dialog v-model="dialog" max-width="500px">
<template v-slot:activator="{ on }">
<v-btn color="primary" dark class="mb-2" v-on="on">New Item</v-btn>
</template>
<v-card>
<v-card-title>
<span class="headline">{{ formTitle }}</span>
</v-card-title>
<v-card-text>
<v-container>
<!-- Form Tag -->
<v-form
id="dessertForm"
ref="dessertForm"
v-model="isValid"
>
<v-row>
<v-col cols="12" sm="6" md="4">
<v-text-field :rules="nameRules" v-model="editedItem.name" label="Dessert name"></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field :rules="genericRules" v-model="editedItem.calories" label="Calories"></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field :rules="genericRules" v-model="editedItem.fat" label="Fat (g)"></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field :rules="genericRules" v-model="editedItem.carbs" label="Carbs (g)"></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field :rules="genericRules" v-model="editedItem.protein" label="Protein (g)"></v-text-field>
</v-col>
</v-row>
</v-form>
</v-container>
</v-card-text>
<v-card-actions>
<div class="flex-grow-1"></div>
<v-btn color="blue darken-1" text #click="close">Cancel</v-btn>
<!-- Disable button if is not valid -->
<v-btn :disabled="!isValid" color="blue darken-1" text #click="save">Save</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-toolbar>
</template>
<template v-slot:item.action="{ item }">
<v-icon
small
class="mr-2"
#click="editItem(item)"
>
edit
</v-icon>
<v-icon
small
#click="deleteItem(item)"
>
delete
</v-icon>
</template>
<template v-slot:no-data>
<v-btn color="primary" #click="initialize">Reset</v-btn>
</template>
</v-data-table>
</v-app>
</div>
Script
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
dialog: false,
isValid: true,
// Rules for name (example)
nameRules: [
v => !!v || 'Name is required',
v => (v && v.length >= 10) || 'Name must be more than 10 characters',
],
// Rules for generic fields (example)
genericRules: [
v => (v && v > 0) || 'Value must be more than 0',
],
headers: [
{
text: 'Dessert (100g serving)',
align: 'left',
sortable: false,
value: 'name',
},
{ text: 'Calories', value: 'calories' },
{ text: 'Fat (g)', value: 'fat' },
{ text: 'Carbs (g)', value: 'carbs' },
{ text: 'Protein (g)', value: 'protein' },
{ text: 'Actions', value: 'action', sortable: false },
],
desserts: [],
editedIndex: -1,
editedItem: {
name: '',
calories: 0,
fat: 0,
carbs: 0,
protein: 0,
},
defaultItem: {
name: '',
calories: 0,
fat: 0,
carbs: 0,
protein: 0,
},
}),
computed: {
formTitle () {
return this.editedIndex === -1 ? 'New Item' : 'Edit Item'
},
},
watch: {
dialog (val) {
val || this.close()
},
},
mounted () {
this.initialize()
},
methods: {
initialize () {
this.desserts = [
{
name: 'Frozen Yogurt',
calories: 159,
fat: 6.0,
carbs: 24,
protein: 4.0,
},
{
name: 'Ice cream sandwich',
calories: 237,
fat: 9.0,
carbs: 37,
protein: 4.3,
},
{
name: 'Eclair',
calories: 262,
fat: 16.0,
carbs: 23,
protein: 6.0,
},
{
name: 'Cupcake',
calories: 305,
fat: 3.7,
carbs: 67,
protein: 4.3,
},
{
name: 'Gingerbread',
calories: 356,
fat: 16.0,
carbs: 49,
protein: 3.9,
},
{
name: 'Jelly bean',
calories: 375,
fat: 0.0,
carbs: 94,
protein: 0.0,
},
{
name: 'Lollipop',
calories: 392,
fat: 0.2,
carbs: 98,
protein: 0,
},
{
name: 'Honeycomb',
calories: 408,
fat: 3.2,
carbs: 87,
protein: 6.5,
},
{
name: 'Donut',
calories: 452,
fat: 25.0,
carbs: 51,
protein: 4.9,
},
{
name: 'KitKat',
calories: 518,
fat: 26.0,
carbs: 65,
protein: 7,
},
]
},
editItem (item) {
this.editedIndex = this.desserts.indexOf(item)
this.editedItem = Object.assign({}, item)
this.dialog = true
},
deleteItem (item) {
const index = this.desserts.indexOf(item)
confirm('Are you sure you want to delete this item?') && this.desserts.splice(index, 1)
},
close () {
this.dialog = false
setTimeout(() => {
this.editedItem = Object.assign({}, this.defaultItem)
this.editedIndex = -1
}, 300)
},
save () {
if(this.$refs.dessertForm.validate()) {
if (this.editedIndex > -1) {
Object.assign(this.desserts[this.editedIndex], this.editedItem)
} else {
this.desserts.push(this.editedItem)
}
this.close()
}
},
},
})
It looks like I did get it working with nested v-forms. I updated the codepen to show the solution. If you edit a row and remove the dessert name the save button will be disabled. If there are no table rows or the other v-text-field has no text the continue button will be disabled.
Related
I have an appBar(mobile) with some icons on it. I want to show a dropdown when I click the notifications icon (as seen in many mobile apps) with the list of notifications. I am absolutely new to Vue.
<template>
<div>
<!-- <v-app-bar color="deep-purple accent-4" dense dark> -->
<v-menu>
<template v-slot:activator="{ on: activationEvents }">
<v-btn icon v-bind="attrs" v-on="activationEvents">
<div class="notification" #click="showNotif" v-bind="attrs" v-on="on">
<fas class="notif-icon" :icon="['far', 'bell']" />
<span class="count" v-if="notification.unreadCount != 0">{{
notification.unreadCount.toLocaleString()
}}</span>
</div>
</v-btn>
</template>
<v-list>
<v-list-item
v-for="dessert in desserts"
:key="dessert"
#click="() => {}"
>
<v-list-item-title> {{ dessert.name }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
<!-- </v-app-bar> -->
</div>
</template>
export default {
data() {
return {
toggleSelect: false,
desserts: [
{
name: "Frozen Yogurt",
calories: 159,
fat: 6.0,
},
{
name: "Ice cream sandwich",
calories: 237,
fat: 9.0,
},
{
name: "Eclair",
calories: 262,
fat: 16.0,
},
{
name: "Cupcake",
calories: 305,
fat: 3.7,
},
{
name: "Gingerbread",
calories: 356,
fat: 16.0,
},
{
name: "Jelly bean",
calories: 375,
fat: 0.0,
},
{
name: "Lollipop",
calories: 392,
fat: 0.2,
},
{
name: "Honeycomb",
calories: 408,
fat: 3.2,
},
{
name: "Donut",
calories: 452,
fat: 25.0,
},
{
name: "KitKat",
calories: 518,
fat: 26.015,
},
],
notification: { unreadCount: 0 },
};
},
Currently, there are 2 problems:
The list gets displayed directly without even clicking on the icon
The list gets displayed on/in the appbar (instead of getting displayed below the appbar)
Hello how to changes this values in data table, true change to active or false in inactive
add my code in vue js and vuetify i like change value in row state, chage this value true change to active or false in inactive, i'm using axios to connect in api from csharp
next of code i'm add image of i want made it this
<template>
<v-data-table
:headers="headers"
:items="categories"
sort-by="Name"
class="elevation-1"
:search="search"
>
<template v-slot:top>
<v-toolbar flat>
<v-toolbar-title>Categories</v-toolbar-title>
<v-divider class="mx-4" inset vertical></v-divider>
<v-spacer></v-spacer>
<v-text-field
v-model="search"
append-icon="search"
label="Search"
single-line
hide-details
></v-text-field>
<v-spacer></v-spacer>
<v-dialog v-model="dialog" max-width="500px">
<template v-slot:activator="{ on, attrs }">
<v-btn color="primary" dark class="mb-2" v-bind="attrs" v-on="on">
New Item
</v-btn>
</template>
<v-card>
<v-card-title>
<span class="text-h5">{{ formTitle }}</span>
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-col cols="12" sm="6" md="4">
<v-text-field
v-model="editedItem.namecategory"
label="Name"
></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field
v-model="editedItem.descategory"
label="Description"
></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field
v-model="editedItem.numberstatate"
label="State"
></v-text-field>
</v-col>
</v-row>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" text #click="close"> Cancel </v-btn>
<v-btn color="blue darken-1" text #click="save"> Save </v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<v-dialog v-model="dialogDelete" max-width="500px">
<v-card>
<v-card-title class="text-h5"
>Are you sure you want to delete this item?</v-card-title
>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" text #click="closeDelete"
>Cancel</v-btn
>
<v-btn color="blue darken-1" text #click="deleteItemConfirm"
>OK</v-btn
>
<v-spacer></v-spacer>
</v-card-actions>
</v-card>
</v-dialog>
</v-toolbar>
</template>
<template v-slot:item.actions="{ item }">
<v-icon small class="mr-2" #click="editItem(item)"> edit </v-icon>
<v-icon small #click="deleteItem(item)"> delete </v-icon>
</template>
<template v-slot:no-data>
<v-btn color="primary" #click="initialize"> Reset </v-btn>
</template>
<script>
import axios from "axios";
export default {
data: () => ({
categories: [],
search: "",
dialog: false,
dialogDelete: false,
headers: [
{ text: "Actions", value: "actions", sortable: false},
{ text: "Name", value: "namecategory" },
{ text: "Description", value: "descategory", sortable: false },
{ text: "State", value: "numberstatate" , sortable: false},
],
desserts: [],
editedIndex: -1,
editedItem: {
name: "",
calories: 0,
fat: 0,
carbs: 0,
protein: 0,
},
defaultItem: {
name: "",
calories: 0,
fat: 0,
carbs: 0,
protein: 0,
},
}),
computed: {
formTitle() {
return this.editedIndex === -1 ? "New Item" : "Edit Item";
},
},
watch: {
dialog(val) {
val || this.close();
},
dialogDelete(val) {
val || this.closeDelete();
},
},
created() {
this.initialize();
this.tolist();
},
methods: {
tolist() {
let me = this;
axios
.get("api/categories/tolist/")
.then(function (response) {
me.categories = response.data;
//console.log(response)
})
.catch(function (error) {
console.log(error);
});
},
initialize() {
this.desserts = [
{
name: "Frozen Yogurt",
calories: 159,
fat: 6.0,
carbs: 24,
protein: 4.0,
},
{
name: "Ice cream sandwich",
calories: 237,
fat: 9.0,
carbs: 37,
protein: 4.3,
},
{
name: "Eclair",
calories: 262,
fat: 16.0,
carbs: 23,
protein: 6.0,
},
{
name: "Cupcake",
calories: 305,
fat: 3.7,
carbs: 67,
protein: 4.3,
},
{
name: "Gingerbread",
calories: 356,
fat: 16.0,
carbs: 49,
protein: 3.9,
},
{
name: "Jelly bean",
calories: 375,
fat: 0.0,
carbs: 94,
protein: 0.0,
},
{
name: "Lollipop",
calories: 392,
fat: 0.2,
carbs: 98,
protein: 0,
},
{
name: "Honeycomb",
calories: 408,
fat: 3.2,
carbs: 87,
protein: 6.5,
},
{
name: "Donut",
calories: 452,
fat: 25.0,
carbs: 51,
protein: 4.9,
},
{
name: "KitKat",
calories: 518,
fat: 26.0,
carbs: 65,
protein: 7,
},
];
},
editItem(item) {
this.editedIndex = this.desserts.indexOf(item);
this.editedItem = Object.assign({}, item);
this.dialog = true;
},
deleteItem(item) {
this.editedIndex = this.desserts.indexOf(item);
this.editedItem = Object.assign({}, item);
this.dialogDelete = true;
},
deleteItemConfirm() {
this.desserts.splice(this.editedIndex, 1);
this.closeDelete();
},
close() {
this.dialog = false;
this.$nextTick(() => {
this.editedItem = Object.assign({}, this.defaultItem);
this.editedIndex = -1;
});
},
closeDelete() {
this.dialogDelete = false;
this.$nextTick(() => {
this.editedItem = Object.assign({}, this.defaultItem);
this.editedIndex = -1;
});
},
save() {
if (this.editedIndex > -1) {
Object.assign(this.desserts[this.editedIndex], this.editedItem);
} else {
this.desserts.push(this.editedItem);
}
this.close();
},
},
};
</script>
and my view is this
enter image description here
I have solved it in the following way, add next line after of data-table
<template v-slot:item.numberstatate="{ item }">
<span v-if="item.numberstatate" class="blue--text">Active</span>
<span v-else class="red--text">insactive</span>
</template
So that it is as follows:
<v-data-table
:headers="headers"
:items="categories"
sort-by="Name"
class="elevation-1"
:search="search"
>
<template v-slot:item.actions="{ item }">
<v-icon small class="mr-2" #click="editItem(item)"> edit </v-icon>
<v-icon small #click="deleteItem(item)"> delete </v-icon>
</template> ........
I'm new in vue.js, please help. I need filters in my table. Each column must have multiple select filter by values. When we click on column header - dropdown multi select opens, and we can select filtered values. For example like this Vuetify Data Table Inline Filter but this example doesn't work with vuetify 2.
My html:
<template>
<v-card class="elevation-3">
<v-card-title>
{{ other_title }}
<v-btn style="background-color: white; box-shadow: none;" #click="csvExport(other_title, otherIncidentsData)">
CSV<i class="fas fa-file-csv"></i>
</v-btn>
<v-btn text #click="exportToPdf()">
Pdf<v-icon>mdi-file-pdf-box-outline</v-icon>
</v-btn>
<v-btn text #click="showExportModal">
<v-icon>mdi-email-receive-outline</v-icon>
</v-btn>
<v-spacer></v-spacer>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="Search"
single-line
hide-details
></v-text-field>
</v-card-title>
<v-data-table
:search="search"
:headers="headers"
:items="otherIncidents"
:items-per-page="10"
>
<template v-slot:item.priority.name="{ item }">
<v-chip :color="getPriorityColor(item.priority.name)" dark>{{ item.priority.name }}</v-chip>
</template>
</v-data-table>
</v-card>
</template>
And JS:
export default {
mixins: [
mixin
],
data() {
return {
search: '',
title: 'MediaMyne reports',
project_title: 'PROJECTS (open at the end of the reporting period)',
new_title: 'NEW REQUESTS (created during the reporting period)',
other_title: 'OTHER REQUESTS (remaining open or changed during the reporting period)',
tabs: [
{ name: 'Projects' },
{ name: 'New Requests' },
{ name: 'Other Requests' },
],
headers: [
{
text: 'Company', align: 'start', sortable: true, value: 'customer.name', width: '14%',
},
{
text: 'Name (Costumer Contact)', align: 'start', sortable: true, value: 'reported_By_Customer_Contact.name', width: '16%',
},
{
text: 'Title', align: 'start', sortable: true, value: 'name', width: '17%',
},
{
text: 'Days open', align: 'center', sortable: true, value: 'daysOpen', width: '9%',
},
{
text: 'Days waiting', align: 'center', sortable: true, value: 'daysWaiting', width: '10%',
},
{
text: 'Workflow step', align: 'start', sortable: true, value: 'workflow_Step.name', width: '12%',
},
{
text: 'Support type', align: 'start', sortable: true, value: 'custom_Fields.customFields.custom_266', width: '12%',
},
{
text: 'Priority', align: 'start', sortable: true, value: 'priority.name', width: '10%',
},
],
otherIncidents: [],
newIncidents: [],
projectIncidents: [],
activeTab: 0,
pdfReportTitle: ''
};
},
components: {
ExportModal
},
computed: {
...mapGetters([
'isLoggedIn'
]),
otherIncidentsData() {
return this.otherIncidents.map(item => ({
Company: item.customer.name,
Costumer_contact_name: item.reported_By_Customer_Contact.name,
Title: item.name,
Days_open: item.daysOpen,
Days_waiting: item.daysWaiting,
Workflow_step: item.workflow_Step.name,
Support_type: item.custom_Fields.customFields.custom_266,
Priority: item.priority.name
}));
},
methods: {
...mapActions([
'setLoginState'
]),
getPriorityColor(priority) {
switch (priority.toLowerCase()) {
case 'critical':
return '#fc0000';
case 'high':
return '#c20202';
case 'normal':
return '#dd7417';
case 'low':
return '#318d14';
default:
return 'rgb(0,0,0,0)';
}
}
};
I'm making this on codepen these days, since I haven't found any nice example:
https://codepen.io/manuel-84/pen/NWxLLmN (work in progress)
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
dialog: false,
filters: { 'name': [], 'calories': [], 'status': [] },
activeFilters: {},
desserts: [],
editedIndex: -1,
editedItem: {
name: '',
calories: 0,
fat: 0,
carbs: 0,
protein: 0,
},
defaultItem: {
name: '',
calories: 0,
fat: 0,
carbs: 0,
protein: 0,
},
}),
computed: {
headers () {
return [
{
text: 'Dessert (100g serving)',
align: 'start',
sortable: true,
value: 'name',
filter: value => {
return this.activeFilters.name.includes(value);
}
},
{ text: 'Calories', value: 'calories',
filter: value => {
return this.activeFilters.calories.includes(value);
}
},
{ text: 'Status', value: 'status',
filter: value => {
return this.activeFilters.status.includes(value);
}
},
{ text: 'Fat (g)', value: 'fat' },
{ text: 'Carbs (g)', value: 'carbs' },
{ text: 'Protein (g)', value: 'protein' },
{ text: 'Actions', value: 'actions', sortable: false },
]
},
formTitle () {
return this.editedIndex === -1 ? 'New Item' : 'Edit Item'
},
},
watch: {
dialog (val) {
val || this.close()
},
/*filters: {
deep: true,
handler(val) {
console.log(val)
}
}*/
},
created () {
this.initialize()
},
methods: {
initialize () {
this.desserts = [
{
name: 'Frozen Yogurt',
calories: 159,
fat: 6.0,
carbs: 24,
protein: 4.0,
status: 'DIET'
},
{
name: 'Ice cream sandwich',
calories: 237,
fat: 9.0,
carbs: 37,
protein: 4.3,
status: 'NO DIET'
},
{
name: 'Eclair',
calories: 262,
fat: 16.0,
carbs: 23,
protein: 6.0,
status: 'DIET'
},
{
name: 'Cupcake',
calories: 305,
fat: 3.7,
carbs: 67,
protein: 4.3,
status: 'NO DIET'
},
{
name: 'Gingerbread',
calories: 356,
fat: 16.0,
carbs: 49,
protein: 3.9,
status: 'DIET'
},
{
name: 'Jelly bean',
calories: 375,
fat: 0.0,
carbs: 94,
protein: 0.0,
status: 'NO DIET'
},
{
name: 'Lollipop',
calories: 392,
fat: 0.2,
carbs: 98,
protein: 0,
status: 'NO DIET'
},
{
name: 'Honeycomb',
calories: 408,
fat: 3.2,
carbs: 87,
protein: 6.5,
status: 'NO DIET'
},
{
name: 'Donut',
calories: 452,
fat: 25.0,
carbs: 51,
protein: 4.9,
status: 'DIET'
},
{
name: 'KitKat',
calories: 518,
fat: 26.0,
carbs: 65,
protein: 7,
status: 'NO DIET'
},
];
for (col in this.filters) {
this.filters[col] = this.desserts.map((d) => { return d[col] }).filter(
(value, index, self) => { return self.indexOf(value) === index }
);
}
this.activeFilters = Object.assign({}, this.filters)
},
toggleAll (col) {
this.activeFilters[col] = this.desserts.map((d) => { return d[col] }).filter(
(value, index, self) => { return self.indexOf(value) === index }
)
},
clearAll (col) {
this.activeFilters[col] = []
},
editItem (item) {
this.editedIndex = this.desserts.indexOf(item)
this.editedItem = Object.assign({}, item)
this.dialog = true
},
deleteItem (item) {
const index = this.desserts.indexOf(item)
confirm('Are you sure you want to delete this item?') && this.desserts.splice(index, 1)
},
close () {
this.dialog = false
this.$nextTick(() => {
this.editedItem = Object.assign({}, this.defaultItem)
this.editedIndex = -1
})
},
save () {
if (this.editedIndex > -1) {
Object.assign(this.desserts[this.editedIndex], this.editedItem)
} else {
this.desserts.push(this.editedItem)
}
this.close()
},
},
})
.v-list--dense .v-list-item, .v-list-item--dense {
min-height: 20px !important;
height: 2rem;
}
.v-application--is-ltr .v-list-item__action:first-child, .v-application--is-ltr .v-list-item__icon:first-child {
margin-right: .5rem !important;
}
<script src="https://cdn.jsdelivr.net/npm/babel-polyfill/dist/polyfill.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.3.4/dist/vuetify.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.3.4/dist/vuetify.min.css" rel="stylesheet"/>
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#5.x/css/materialdesignicons.min.css" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css?family=Material+Icons" rel="stylesheet"/>
<span id="app">
<v-app id="inspire">
<v-data-table :headers="headers" :items="desserts" sort-by="calories" item-key="name" show-select>
<template v-for="(col, i) in filters" v-slot:[`header.${i}`]="{ header }">
<div style="display: inline-block; padding: 16px 0;">{{ header.text }}</div>
<div style="float: right; margin-top: 8px">
<v-menu :close-on-content-click="false" :nudge-width="200" offset-y transition="slide-y-transition" left fixed style="position: absolute; right: 0">
<template v-slot:activator="{ on, attrs }">
<v-btn color="indigo" icon v-bind="attrs" v-on="on">
<v-icon small
:color="activeFilters[header.value].length < filters[header.value].length ? 'red' : 'default'">
mdi-filter-variant
</v-icon>
</v-btn>
</template>
<v-list flat dense class="pa-0">
<v-list-item-group multiple v-model="activeFilters[header.value]" class="py-2">
<template v-for="(item, i) in filters[header.value]">
<v-list-item :key="`item-${i}`" :value="item" :ripple="false">
<template v-slot:default="{ active, toggle }">
<v-list-item-action>
<v-checkbox :input-value="active" :true-value="item"
#click="toggle" color="primary" dense></v-checkbox>
</v-list-item-action>
<v-list-item-content>
<v-list-item-title v-text="item"></v-list-item-title>
</v-list-item-content>
</template>
</v-list-item>
</template>
</v-list-item-group>
<v-divider></v-divider>
<v-btn text block #click="toggleAll(header.value)">Toggle all</v-btn>
<v-btn text block #click="clearAll(header.value)">Clear all</v-btn>
</v-list>
</v-menu>
</div>
</template>
<template v-slot:header="{ props: { headers } }">
<thead>
<tr>
<th :colspan="headers.length">
This is a header
</th>
</tr>
</thead>
</template>
<template v-slot:top>
<v-toolbar flat color="white">
<v-toolbar-title>My CRUD</v-toolbar-title>
<v-divider class="mx-4" inset vertical></v-divider>
<v-spacer></v-spacer>
<v-dialog v-model="dialog" max-width="500px">
<template v-slot:activator="{ on, attrs }">
<v-btn color="primary" dark class="mb-2" v-bind="attrs" v-on="on">New Item</v-btn>
</template>
<v-card>
<v-card-title>
<span class="headline">{{ formTitle }}</span>
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="editedItem.name" label="Dessert name"></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="editedItem.calories" label="Calories"></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="editedItem.fat" label="Fat (g)"></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="editedItem.carbs" label="Carbs (g)"></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="editedItem.protein" label="Protein (g)"></v-text-field>
</v-col>
</v-row>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" text #click="close">Cancel</v-btn>
<v-btn color="blue darken-1" text #click="save">Save</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-toolbar>
</template>
<template v-slot:item.actions="{ item }">
<v-icon small class="mr-2" #click="editItem(item)">
mdi-pencil
</v-icon>
<v-icon small #click="deleteItem(item)">
mdi-delete
</v-icon>
</template>
<template v-slot:no-data>
<v-btn color="primary" #click="initialize">Reset</v-btn>
</template>
</v-data-table>
</v-app>
</div>
I hope the code is clear, anyway feel free to ask
I am currently trying to pull in the datatable row info into a dialog for the rows with qty input value greater than 0. I know with checkboxes if selected/true, it pulls all the same row information into a selected array to easily use. I'm trying to get this same functionality for input greater than 0.
Got checked boxes row info to get into dialog by using the selected array. Cant seem to figure out how to do the same (get that rows data) with inputs greater than 0
https://codepen.io/anon/pen/xeVZKv?editors=1010
HTML
<div id="app">
<v-app id="inspire">
<v-data-table
:headers="headers"
:items="desserts"
class="elevation-1"
v-model="selected"
item-key="name"
>
<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 v-if="props.item.qty < 2" style="width: 10%">
<v-checkbox v-model="props.selected" primary hide-details></v-checkbox>
</td>
<td v-else style="display:inline-flex;">
<v-text-field
value="0"
onclick="this.select()"
type= "number"
:max="props.item.Qty"
:totalCount="props.item.Qty"
min="0"
:id="'inputCount' + props.index"
style="width:40px; margin-left: 5px;"
#change="counter()"
></v-text-field>
<div style="padding-top: 15px; margin-left: 10px;">of {{ props.item.qty }}</div>
</td>
</template>
<template v-slot:pageText="props">
Lignes {{ props.pageStart }} - {{ props.pageStop }} de {{ props.itemsLength }}
</template>
</v-data-table>
<v-layout row wrap class="">
<v-flex xs4 class="text-xs-left">
</v-flex>
<v-flex xs4 class="text-xs-center">
</v-flex>
<v-flex xs4 class="text-xs-right">
<v-btn v-if="buyCounter < 1" color="primary" class="receiveButton" :disabled="buttonActivate" #click="buyModel()">Receive</v-btn>
<v-btn v-else-if="buyCounter > 0 && buyCounter < 2" color="primary" class="receiveButton" :disabled="buttonActivate" #click="buyModel()">Receive {{ buyCounter }} Part</v-btn>
<v-btn v-else-if="buyCounter > 1" color="primary" class="receiveButton" :disabled="buttonActivate" #click="buyModel()">Receive {{ buyCounter }} Parts</v-btn>
<v-btn v-else color="primary" class="receiveButton" :disabled="buttonActivate">Receive</v-btn>
</v-flex>
</v-layout>
</v-app>
</div>
JS
new Vue({
el: '#app',
data () {
return {
inputCount: 0,
selected: [],
headers: [
{
text: 'Dessert (100g serving)',
align: 'left',
sortable: false,
value: 'name'
},
{ text: 'Calories', value: 'calories' },
{ text: 'Fat (g)', value: 'fat' },
{ text: 'Carbs (g)', value: 'carbs' },
{ text: 'Protein (g)', value: 'protein' },
{ text: 'Qty Available', value: 'qty' }
],
desserts: [
{
name: 'Frozen Yogurt',
calories: 159,
fat: 6.0,
carbs: 24,
protein: 4.0,
qty: 1
},
{
name: 'Ice cream sandwich',
calories: 237,
fat: 9.0,
carbs: 37,
protein: 4.3,
qty: 2
},
{
name: 'Eclair',
calories: 262,
fat: 16.0,
carbs: 23,
protein: 6.0,
qty: 1
},
{
name: 'Cupcake',
calories: 305,
fat: 3.7,
carbs: 67,
protein: 4.3,
qty: 1
},
{
name: 'Gingerbread',
calories: 356,
fat: 16.0,
carbs: 49,
protein: 3.9,
qty: 3
},
{
name: 'Jelly bean',
calories: 375,
fat: 0.0,
carbs: 94,
protein: 0.0,
qty: 1
},
{
name: 'Lollipop',
calories: 392,
fat: 0.2,
carbs: 98,
protein: 0,
qty: 1
},
{
name: 'Honeycomb',
calories: 408,
fat: 3.2,
carbs: 87,
protein: 6.5,
qty: 1
},
{
name: 'Donut',
calories: 452,
fat: 25.0,
carbs: 51,
protein: 4.9,
qty: 1
},
{
name: 'KitKat',
calories: 518,
fat: 26.0,
carbs: 65,
protein: 7,
qty: 1
}
]
};
},
computed: {
buttonActivate: function () {
let count = 0;
$('input[id^="inputCount"]').each(function (i, e) {
let element = $(e).val();
if (element == "") {
element = 0;
}
let eachCountInt = parseInt(element);
count = eachCountInt + count;
})
this.inputCount = count;
if (this.selected.length > 0 || this.inputCount > 0) {
return false;
} else {
return true;
}
},
buyCounter: function () {
let count = this.selected.length + this.inputCount;
return count;
}
},
methods: {
counter() {
let count = 0;
$('input[id^="inputCount"]').each(function (i, e) {
let element = $(e).val();
if (element == "") {
element = 0;
}
let eachCountInt = parseInt(element);
count = eachCountInt + count;;
})
this.inputCount = count;
},
buyModel() {
}
}
})
Overall looking to pull in all selected/greater than 0 info into the dialog to confirm before processing. Any help would be greatly appreciated.
Is it possible to left align the rows per pages and page navigation and put a button in its place in the same row?
The actions row are aligned right by default. There is a way to achieve what you want though. You can use custom pagination and hide the current one:
<v-data-table
:headers="headers"
:items="desserts"
:search="search"
hide-actions
:pagination.sync="pagination"
>
And add this after the datatable:
<v-layout row justify-center>
<v-pagination v-model="pagination.page" :length="pages"></v-pagination>
<v-btn class="test">test</v-btn>
</v-layout>
See more here.
Here is a Codepen in action.
I found the easiest way is to add the button to some data table slot you use and just position the button relatively to the table.
<v-data-table
style="position: relative;">
<template slot="footer">
<v-btn style="position: absolute; left: 10px; bottom: 10px;">
test
</v-btn>
</template>
</v-data-table>
Simplest way by CSS :
template in your datatable :
<template v-slot:actions-prepend>
<v-btn>
Click me !
</v-btn>
</template>
CSS :
.my-grid .v-datatable__actions > div:first-child {
flex: 1;
}
Working snippet :
new Vue({
el: '#app',
methods: {
onClick() { this.dark = !this.dark; }
},
data: {
dark: true,
headers: [{
text: 'Dessert (100g serving)',
value: 'name'
},
{
text: 'Calories',
value: 'calories'
},
{
text: 'Fat (g)',
value: 'fat'
},
{
text: 'Carbs (g)',
value: 'carbs'
},
{
text: 'Protein (g)',
value: 'protein'
},
{
text: 'Iron (%)',
value: 'iron'
}
],
desserts: [{
name: 'Frozen Yogurt',
calories: 159,
fat: 6.0,
carbs: 24,
protein: 4.0,
iron: '1%'
},
{
name: 'Ice cream sandwich',
calories: 237,
fat: 9.0,
carbs: 37,
protein: 4.3,
iron: '1%'
},
{
name: 'Eclair',
calories: 262,
fat: 16.0,
carbs: 23,
protein: 6.0,
iron: '7%'
},
{
name: 'Cupcake',
calories: 305,
fat: 3.7,
carbs: 67,
protein: 4.3,
iron: '8%'
}
]
}
})
.my-grid .v-datatable__actions > div:first-child {
flex: 1;
}
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#1.x/dist/vuetify.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.x/dist/vuetify.js"></script>
<div id="app">
<v-app :dark="dark">
<v-content>
<v-data-table :headers="headers" :items="desserts" class="my-grid">
<template v-slot:items="{item}">
<tr>
<td>{{item.name}}</td>
<td>{{item.calories}}</td>
<td>{{item.fat}}</td>
<td>{{item.carbs}}</td>
<td>{{item.protein}}</td>
<td>{{item.iron}}</td>
</tr>
</template>
<template v-slot:actions-prepend>
<v-btn #click="onClick">
Switch mode
</v-btn>
</template>
</v-data-table>
</v-content>
</v-app>
</div>