Валидация форм

Валидация форм является важной частью веб-приложений, обеспечивая корректность и полноту данных, вводимых пользователем. Во Vue.js она реализуется через комбинацию реактивных данных, вычисляемых свойств и методов компонентов.

Двусторонняя привязка данных

Ключевой механизм — директива v-model, обеспечивающая двустороннюю привязку данных формы к свойствам компонента. Например:

data() {
  return {
    username: '',
    email: ''
  };
}
<input v-model="username" type="text" />
<input v-model="email" type="email" />

Любые изменения в этих полях автоматически обновляют состояние компонента. Валидация строится поверх этих реактивных свойств.

Простая синхронная валидация

Для базовой проверки можно использовать вычисляемые свойства:

computed: {
  isUsernameValid() {
    return this.username.length >= 3;
  },
  isEmailValid() {
    const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return re.test(this.email);
  },
  isFormValid() {
    return this.isUsernameValid && this.isEmailValid;
  }
}

С помощью этих свойств можно управлять отображением ошибок:

<span v-if="!isUsernameValid">Имя должно содержать минимум 3 символа</span>
<span v-if="!isEmailValid">Некорректный формат email</span>
<button :disabled="!isFormValid">Отправить</button>

Асинхронная валидация

Часто необходимо проверять данные на сервере, например уникальность email. Для этого используются методы компонента и async/await:

methods: {
  async checkEmail() {
    if (!this.email) return false;
    const response = await fetch(`/api/check-email?email=${this.email}`);
    const result = await response.json();
    return result.available;
  }
}

Вызов метода можно интегрировать в обработчик события @blur:

<input v-model="email" @blur="validateEmail" />
methods: {
  async validateEmail() {
    this.emailValid = await this.checkEmail();
  }
}

Использование сторонних библиотек

Для сложных форм удобно применять специализированные библиотеки, например Vuelidate или vee-validate, которые позволяют:

  • задавать правила валидации декларативно;
  • объединять синхронные и асинхронные проверки;
  • управлять состоянием полей ($dirty, $error, $invalid).

Пример с Vuelidate:

import { required, minLength, email } from '@vuelidate/validators'
import { useVuelidate } from '@vuelidate/core'

export default {
  setup() {
    const state = reactive({
      username: '',
      email: ''
    })

    const rules = {
      username: { required, minLength: minLength(3) },
      email: { required, email }
    }

    const v$ = useVuelidate(rules, state)

    return { state, v$ }
  }
}
<input v-model="state.username" />
<span v-if="v$.username.$error">Имя некорректно</span>

<input v-model="state.email" />
<span v-if="v$.email.$error">Email некорректен</span>

Реактивное отображение ошибок

Во Vue.js ошибки валидации удобно хранить в объекте:

data() {
  return {
    errors: {}
  }
},
methods: {
  validateField(field, value) {
    switch(field) {
      case 'username':
        this.errors.username = value.length < 3 ? 'Имя слишком короткое' : '';
        break;
      case 'email':
        const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        this.errors.email = !re.test(value) ? 'Неверный email' : '';
        break;
    }
  }
}

Привязка к форме:

<input v-model="username" @input="validateField('username', username)" />
<span>{{ errors.username }}</span>

<input v-model="email" @input="validateField('email', email)" />
<span>{{ errors.email }}</span>

Глобальная и локальная валидация

Валидацию можно выносить в отдельные компоненты или миксины для повторного использования:

export const validationMixin = {
  methods: {
    required(value) {
      return value ? '' : 'Поле обязательно для заполнения';
    },
    minLength(value, length) {
      return value.length >= length ? '' : `Минимум ${length} символов`;
    }
  }
}

Компонент:

mixins: [validationMixin],
methods: {
  validateUsername() {
    this.errors.username = this.required(this.username) || this.minLength(this.username, 3);
  }
}

Реактивные формы с Composition API

С использованием Composition API можно объединять состояние, методы и правила в единый объект:

import { reactive, computed } from 'vue'

export default {
  setup() {
    const form = reactive({
      username: '',
      email: '',
      errors: {}
    })

    const validate = () => {
      form.errors.username = form.username.length < 3 ? 'Имя слишком короткое' : '';
      const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
      form.errors.email = !re.test(form.email) ? 'Email некорректен' : '';
    }

    const isFormValid = computed(() => !form.errors.username && !form.errors.email)

    return { form, validate, isFormValid }
  }
}

В шаблоне:

<input v-model="form.username" @input="validate" />
<span>{{ form.errors.username }}</span>

<input v-model="form.email" @input="validate" />
<span>{{ form.errors.email }}</span>

<button :disabled="!isFormValid">Отправить</button>

Совмещение нескольких стратегий

В реальных приложениях часто используют комбинацию:

  • базовая синхронная проверка на стороне клиента;
  • асинхронная проверка через API;
  • централизованное управление ошибками через состояние компонента;
  • сторонние библиотеки для сложной логики.

Такой подход обеспечивает надежность, читаемость кода и легкость расширения форм.