I would like to add Vuelidate validation to an editable field in a Vuetify data table. However, I do not know how to make it work with the props.item parameter.
For a normal input field I would do something like
:error-messages="qtyErrors"
@input="$v.quantity.$touch()"
@blur="$v.quantity.$touch()"
.
.
validations: {
quantity: { required, numeric }
}
I do not know how to make this work for props.item.squareFootage. I am not sure how to get a handle on the index. Here is my data table. Any suggestions would be appreciated.
<v-data-table
:headers="bldgHeaders"
:items="selectedBldgs"
:pagination.sync="paginationSelected"
class="elevation-1"
>
<template slot="items" slot-scope="props">
<tr>
<td>{{ props.item.buildingNumber }}</td>
<td>{{ props.item.description }}</td>
<td>
<v-edit-dialog
:return-value.sync="props.item.squareFootage"
lazy
large
> {{ props.item.squareFootage }}
<v-text-field
slot="input"
v-model="props.item.squareFootage"
label="Edit"
single-line
></v-text-field>
</v-edit-dialog>
</td>
</tr>
</template>
You don't want to render a dialog for each row in your datatable. Render one dialog on the page and keep track of which row the user is editing in a data property. This property is called "currentItem" in the below snippet. You can then bind your validations to the properties in just that one object instead of creating validations for every row in the table. If you don't want to use v-dialog you can also use v-menu positioned absolutely without an external activator.
const {
required,
maxLength,
email
} = validators
const validationMixin = vuelidate.validationMixin
Vue.use(vuelidate.default)
new Vue({
el: '#app',
data() {
return {
editDialog: false,
currentItem: {},
headers: [{
text: 'Dessert (100g serving)',
align: 'left',
sortable: false,
value: 'name'
},
{
text: 'Calories',
value: 'calories'
},
{
text: 'Fat (g)',
value: 'fat'
}
],
desserts: [{
id: 1,
name: 'Frozen Yogurt',
calories: 159,
fat: 6.0
},
{
id: 2,
name: 'Ice cream sandwich',
calories: 237,
fat: 9.0
}
]
}
},
validations: {
currentItem: {
fat: {
required
}
}
},
methods: {
openEditDialog(item) {
this.currentItem = Object.assign({}, item)
this.editDialog = true
},
validate() {
this.$v.currentItem.fat.$touch()
if (!this.$v.currentItem.fat.$error) this.editDialog = false
}
}
})
<script src="https://unpkg.com/vuelidate/dist/validators.min.js"></script>
<script src="https://unpkg.com/vuelidate/dist/vuelidate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<!DOCTYPE html>
<html>
<head>
<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/dist/vuetify.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
</head>
<body>
<div id="app">
<v-app>
<v-content>
<v-container>
<v-data-table :headers="headers" :items="desserts" class="elevation-1">
<template slot="items" slot-scope="props">
<tr @click="openEditDialog(props.item)">
<td>{{ props.item.name }}</td>
<td class="text-xs-center">{{ props.item.calories }}</td>
<td class="text-xs-center">{{ props.item.fat }}</td>
</tr>
</template>
</v-data-table>
</v-container>
<v-dialog v-model="editDialog" width="500">
<v-card>
<v-card-title class="headline grey lighten-2" primary-title>
Set Fat Content
</v-card-title>
<v-card-text>
<v-form>
<v-text-field label="Fat" v-model="currentItem.fat" required :error="$v.currentItem.fat.$dirty && $v.currentItem.fat.$error">
</v-text-field>
</v-form>
</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="primary" flat @click="validate()">
Save
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-content>
</v-app>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.js"></script>
</body>
</html>