Как использовать Vuelidate с редактируемым полем Vuetify Data Table
Я хотел бы добавить проверку Vuelidate в редактируемое поле в таблице данных Vuetify. Однако я не знаю, как заставить его работать с параметром props.item.
Для обычного поля ввода я бы сделал что-то вроде
:error-messages="qtyErrors"
@input="$v.quantity.$touch()"
@blur="$v.quantity.$touch()"
.
.
validations: {
quantity: { required, numeric }
}
Я не знаю, как заставить это работать для props.item.squareFootage. Я не уверен, как получить указатель на индекс. Вот моя таблица данных. Мы ценим любые предложения.
<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>
1 ответ
Вы не хотите отображать диалог для каждой строки в вашей таблице данных. Визуализируйте один диалог на странице и отслеживайте, какую строку пользователь редактирует в свойстве данных. Это свойство называется currentItem в приведенном ниже фрагменте. Затем вы можете привязать свои проверки к свойствам только этого одного объекта вместо создания проверок для каждой строки в таблице. Если вы не хотите использовать v-dialog, вы также можете использовать v-menu, расположенное абсолютно без внешнего активатора.
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>
Библиотека vuelidate поддерживает Collection с ключевым словом $iter. Вам нужно только объявить структуру данных вашего элемента с помощью ключевого слова $iter, и vuelidate создаст объект проверки для каждого элемента. Например, вы можете проверить здесь https://vuelidate.js.org/.
Однако проблема возникает при доступе к этому объекту проверки в шаблоне строки v-data-table. Поскольку сгенерированные объекты проверки доступны только для итератора, это не список, код в шаблоне строки должен иметь способ доступа к этим объектам проверки. Здесь я работал с двумя вспомогательными функциями: getV() для доступа к объекту проверки и модели данных объекта проверки getVModel().
getIter () {
return Object.values(this.$v.items.$each.$iter)
},
getV (item) {
if (!item.vLink) item.vLink = this.getIter().filter(i => i.$model.Id === item.Id)[0]
return item.vLink
},
getVModel (item) {
return this.getV(item).$model
},
Затем в шаблоне vue класс шаблона строки: может использовать getV () для доступа к $invalid или $dirty, а v-модель шаблона строки может использовать getVModel() для доступа к данным.
Вот пример:
v-data-table(:headers="headers" :items="items" item-key="Id")
template(v-slot:body="{ items }")
tbody
tr(v-for="item in items" :key="item.Id" :class="{ vmodified: !getV(item).$invalid && getV(item).$dirty, verror: getV(item).$invalid }")
td
input.table-input(type='text' v-model="getVModel(item).Vendor" @input="onChangeItem(getV(item))")
td
input.table-input(type='text' v-model="getVModel(item).Material" @input="onChangeItem(getV(item))")