我正在查看CRUD操作的vuetify表示例。这里是一个codepen: https://codepen.io/uglyhobbitfeet/pen/oNvKaaL
我添加了一个环绕表格和另一个组件的父窗体。它会验证该表具有多于0行,并在用户单击“继续”按钮之前验证其他组件。
在提供的代码笔中,当用户单击“新建项”按钮(位于表的右上方)时,我想先验证弹出的字段,然后用户才能将数据“保存”到桌子。怎么办?我试图将v-对话框与父窗体分开包装在单独的v-窗体中,但是我无法使其正常工作,而且我不确定嵌套窗体是否可行。有什么建议吗?
由于SO需要在提供Codepen链接时发布代码,因此以下是一小段代码。
<v-data-table
:headers="headers"
:items="desserts"
sort-by="calories"
class="elevation-1"
>
答案 0 :(得分:0)
我得到了代码并进行了一些更改,我在de v-container中添加了v-form进行验证,然后为输入进行验证创建了规则,如果该窗体对两个编辑均无效,则禁用该按钮和新项目。 在第二部分的脚本中,我添加了用于验证字段的规则,然后在保存功能中检查了一切是否正常。
在另一种形式(父亲形式)中,您只需要验证是否添加了某些项目,因为您已经在对话框中验证了表单中的数据。
this.desserts.length > 0 //And then proceed
有关更多信息,请参见docs
模板
<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>
脚本
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()
}
},
},
})
答案 1 :(得分:0)
看起来我确实可以使用嵌套v表单。我更新了代码笔以显示解决方案。如果您编辑一行并删除甜点名称,则保存按钮将被禁用。如果没有表格行或另一个v-文本字段没有文本,则继续按钮将被禁用。
答案 2 :(得分:0)
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();
}
},
将您的子组件分配给父输入。
来源:https://github.com/vuetifyjs/vuetify/issues/4900#issuecomment-423600028