Отображение сообщений об ошибках в vuetify при проверке вложенного объекта с помощью vuelidate

Я использую vuelidate для проверки ввода формы и отображения сообщений об ошибках с помощью 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
      },

Пример метода (Update, также пробовал метод с передачей индекса):

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. Разница заключается не в цикле в шаблоне, который я хотел бы генерировать сообщением программно.

ССЫЛКА

Пример предоставлен 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 ответ

Решение

ЧТО ПОШЛО НЕ ТАК

  1. Общее вычисляемое свойство, которое вызывает проблему, когда все братья и сестры имеют одно и то же сообщение об ошибке. (Решено, написав это в строке)
  2. Реактивность не вызвана из-за того, что массив не обновляется "реактивным способом" (в этом случае обратите внимание на предостережения об обнаружении изменений вместо обновления индекса: я копирую массив, заменяю элемент и затем задаю весь массив.)
  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 внутри компонента. Таким образом, вы можете сохранить вычисленное сообщение об ошибке, так как оно не будет передано.

Другие вопросы по тегам