使用 vuelidate 验证嵌套对象时在 vuetify 中显示错误消息

Displaying error messages in vuetify when validating nested object with vuelidate

我正在使用 vuelidate to validate my form input and display the error messages using vuetifyjs。我设法进行了基本的对象验证并能够显示错误消息。

但是,我在验证集合时遇到了显示错误消息的问题。

问题

示例数据结构:

contact: {
  websites: [
    {
      url: 'http://www.something.com',
      label: 'Website',
    }
  ]
}

示例验证:

validations: {
  websites: {
    $each: {
      url: {
        url,
      }
    }
  },
}

示例模板:

<template v-for="(website, index) in websites">
        <v-layout row :key="`website${index}`">
          <v-flex xs12 sm9 class="pr-3">
            <v-text-field
                    label="Website"
                    :value="website.url"
                    @input="$v.websites.$touch()"
                    @blur="$v.websites.$touch()"
                    :error-messages="websiteErrors"
            ></v-text-field>
          </v-flex>
        </v-layout>
</template>

示例计算错误消息:

websiteErrors() {
        console.log('websites',this.$v.websites) // contains $each
        const errors = []
        if (!this.$v.websites.$dirty) {
          return errors
        }
        // Issue is that all of them show must be valid, even if they are valid. 
        // Validation is basically broken.
        // I also tried this.$v.websites.$each.url
        !this.$v.websites.url && errors.push('Must be valid url')
        return errors
      },

示例方法(更新,也尝试了传递索引的方法):

websiteErrors(index) {
        console.log('this.$v.entity.websites', this.$v.entity.websites.$each.$iter, this.$v.entity.websites.$each.$iter[index], this.$v.entity.websites.minLength, this.$v.entity.websites.$each.$iter[index].url)
        const errors = []
        if (!this.$v.entity.websites.$dirty) {
          return errors
        }

        !this.$v.entity.websites.$each.$iter[index].url && errors.push('Must be valid url')
        return errors
      },

然而,当我这样做时,它总是正确的,因此永远不会显示错误。

预期

我希望使用与 vuelidate sub-collection validation 中相同的示例,不同之处在于我想以编程方式生成消息,而不是在模板中循环。

参考资料

vuelidate 提供的示例:

import { required, minLength } from 'vuelidate/lib/validators'

export default {
  data() {
    return {
      people: [
        {
          name: 'John'
        },
        {
          name: ''
        }
      ]
    }
  },
  validations: {
    people: {
      required,
      minLength: minLength(3),
      $each: {
        name: {
          required,
          minLength: minLength(2)
        }
      }
    }
  }
}

<div>
  <div v-for="(v, index) in $v.people.$each.$iter">
    <div class="form-group" :class="{ 'form-group--error': v.$error }">
      <label class="form__label">Name for {{ index }}</label>
      <input class="form__input" v-model.trim="v.name.$model"/>
    </div>
    <div class="error" v-if="!v.name.required">Name is required.</div>
    <div class="error" v-if="!v.name.minLength">Name must have at least {{ v.name.$params.minLength.min }} letters.</div>
  </div>
  <div>
    <button class="button" @click="people.push({name: ''})">Add</button>
    <button class="button" @click="people.pop()">Remove</button>
  </div>
  <div class="form-group" :class="{ 'form-group--error': $v.people.$error }"></div>
  <div class="error" v-if="!$v.people.minLength">List must have at least {{ $v.people.$params.minLength.min }} elements.</div>
  <div class="error" v-else-if="!$v.people.required">List must not be empty.</div>
  <div class="error" v-else-if="$v.people.$error">List is invalid.</div>
  <button class="button" @click="$v.people.$touch">$touch</button>
  <button class="button" @click="$v.people.$reset">$reset</button>
  <tree-view :data="$v.people" :options="{rootObjectKey: '$v.people', maxDepth: 2}"></tree-view>
</div>

出了什么问题

  1. Shared computed 属性 导致所有兄弟共享相同错误消息的问题。 (通过内联写入解决)
  2. 由于数组未在 "reactive way" 中更新而未触发反应(在这种情况下请务必注意 Change Detection Caveats 而不是更新索引:我复制数组,替换项目然后设置整个数组。)
  3. 错误的使用位置vuelidate $each.$iter:将其从计算错误消息移至v-for

解决方案

这是操作方法(修复 1 和 3):

<template v-for="(v, index) in $v.websites.$each.$iter">
  <v-layout row :key="`website${index}`">
    <v-flex xs12 sm9 class="pr-3">
      <v-text-field
                    label="Website"
                    :value="v.$model.url"
                    @input="$v.websites.$touch()"
                    @blur="$v.websites.$touch()"
                    :error-messages="v.$dirty && !v.required ? ['This field is required'] : !v.url ? ['Must be a valid url'] : []"
      />
    </v-flex>
  </v-layout>
</template>

这就是我现在的更新方法(修复 2):

  updateWebsite(index, $event) {
    const websites = [...this.websites];
    websites[index] = $event;
    this.updateVuex(`websites`, websites)
    this.$v.websites.$touch()
  },

本来是这样的:

  updateWebsite(index, $event) {
    this.updateVuex(`websites[${index}]`, $event)
    this.$v.websites.$touch()
  },

备选方案

还有另一种选择,即在本例中 website 将其包装在一个组件中。这样您就可以保留计算出的错误消息,因为它不会被共享。