Vuetify /嵌套验证

时间:2019-10-02 17:58:23

标签: vue.js vuetify.js

我正在查看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"
>

3 个答案:

答案 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