Валидация формы с помощью AJV, Vue.js и TypeScript

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

Что такое AJV?

AJV (Another JSON Schema Validator) - это быстрая библиотека валидации данных в формате JSON с поддержкой JSON Schema. JSON Schema - это язык описания структуры и валидации данных в формате JSON. AJV позволяет проверять данные по подготовленным схемам валидации.

Подготовка проекта

Прежде чем начать, убедитесь, что у вас уже есть:

  • Node.js v18.16.1.

  • @vue/cli 5.0.8

Давайте создадим проект новый проект с помощью Vue CLI с такими параметрами:

vue create ajv-validation
Валидация формы с помощью AJV, Vue.js и TypeScript

Установим необходимые зависимости в нашем проекте:

npm install ajv ajv-formats ajv-errors

Создание файла схемы валидации login.json

{ "$id": "/login.json", "type": "object", "additionalProperties": false, "required": ["login", "password"], "properties": { "login": { "type": "string", "format": "email", "errorMessage": "enter a valid email address" }, "password": { "type": "string", "minLength": 6, "maxLength": 1024 } } }

В этой схеме мы определяем тип каждого поля (строка), а также устанавливаем некоторые правила валидации, такие как формат email и минимальная длина пароля. Поля "login" и "password" обязательны для заполнения.

Давайте кратко пройдёмся по 2 важным методам:

onLogin — это метод, который вызывается при попытке входа пользователя в систему (логине). Он выполняет проверку и валидацию введенных пользователем данных и предпринимает соответствующие действия в зависимости от результата проверки.

  • Проверка введенных данных на корректность с помощью функции валидации validate. Эта функция использует схему валидации данных и проверяет соответствие данных этой схеме. Возвращается флаг isValid, который указывает, прошла ли валидация успешно.

  • Если данные некорректны (isValid равен false), выполняется обработка ошибок.

function onLogin() { errors.value.clear(); if (validator.validate("/login.json", formData.value)) { // valid, do nothing } else if (validator.errors?.length) { for (const [, e] of validator.errors.entries()) { if (!e.message) { continue; } const fieldName = e.instancePath.substring(1); const fieldErrors: string[] = errors.value.get(fieldName) || []; fieldErrors.push(e.message); errors.value.set(fieldName, fieldErrors); } } }

onBlur — это метод, который вызывается при событии "blur" (потеря фокуса) на текстовом поле ввода формы. Он используется для валидации данных, введенных пользователем, когда пользователь переходит с поля на другой элемент формы или щелкает вне текстового поля.

  • Получить имя (идентификатор) и значение поля ввода, на котором произошло событие "blur".

  • Получить схему валидации для данного поля.

  • Проверка значения поля на корректность с помощью функции валидации validate.
  • Если значение поля некорректно (isValid равен false), выполняется обработка ошибки.

function onBlur(e: any) { const fieldName = e.target.id; const fieldValue = e.target.value; if ( validator.validate(`/login.json#/properties/${fieldName}`, fieldValue) ) { errors.value.delete(fieldName); } else if (validator.errors?.length) { errors.value.set( fieldName, validator.errors.map((e) => e.message) as string[] ); } }

Заключение

Теперь у вас есть пример, как использовать AJV с Vue.js и TypeScript для валидации формы. Это позволяет создавать мощные и гибкие системы валидации, которые помогут улучшить пользовательский опыт и обеспечить корректную обработку данных на сервере.

Исходный код

<template> <div class="login-form"> <h2>Login</h2> <form @submit.prevent="onLogin"> <div class="form-group"> <label for="login">Email</label> <input v-model="formData.login" :class="{ 'is-invalid': isInvalid(errors.has('login')) }" @blur="onBlur" type="text" id="login" placeholder="Enter your email" /> <ul class="error-wrapper"> <li v-for="errorMsg of errors.get('login')" :key="errorMsg"> {{ errorMsg }} </li> </ul> </div> <div class="form-group"> <label for="password">Password</label> <input v-model="formData.password" :class="{ 'is-invalid': isInvalid(errors.has('password')) }" @blur="onBlur" type="password" id="password" placeholder="Enter your password" /> <ul class="error-wrapper"> <li v-for="errorMsg of errors.get('password')" :key="errorMsg"> {{ errorMsg }} </li> </ul> </div> <button type="submit" :disabled="errors.size > 0" @click="submit('ok')"> Login </button> </form> </div> </template> <script lang="ts"> import { defineComponent, ref, watch } from "vue"; import login from "@/schemas/login.json"; import Ajv from "ajv"; import ajvFormats from "ajv-formats"; import ajvErrors from "ajv-errors"; export default defineComponent({ setup() { const formData = ref({ login: "", password: "", }); const opts = { allErrors: true }; const validator = ajvErrors(ajvFormats(new Ajv(opts))); validator.addSchema(login); let errors = ref<Map<string, string[]>>(new Map()); watch( () => errors.value, () => void 0 ); function onBlur(e: any) { const fieldName = e.target.id; const fieldValue = e.target.value; if ( validator.validate(`/login.json#/properties/${fieldName}`, fieldValue) ) { errors.value.delete(fieldName); } else if (validator.errors?.length) { errors.value.set( fieldName, validator.errors.map((e) => e.message) as string[] ); } } function onLogin() { errors.value.clear(); if (validator.validate("/login.json", formData.value)) { // valid, do nothing } else if (validator.errors?.length) { for (const [, e] of validator.errors.entries()) { if (!e.message) { continue; } const fieldName = e.instancePath.substring(1); const fieldErrors: string[] = errors.value.get(fieldName) || []; fieldErrors.push(e.message); errors.value.set(fieldName, fieldErrors); } } } function formColor(error: any) { return error?.length; } function submit(text: string) { alert(text); } return { formData, errors, isInvalid: formColor, onBlur, onLogin, submit, }; }, }); </script> <style> body { font-family: sans-serif; } .login-form { max-width: 300px; margin: 0 auto; padding: 20px; border: 1px solid #ccc; border-radius: 5px; background-color: #f9f9f9; } .login-form h2 { text-align: center; margin-bottom: 20px; } .form-group { margin-bottom: 20px; } label { display: block; font-weight: bold; margin-bottom: 5px; } input { width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 5px; } button { width: 100%; padding: 10px; background-color: #007bff; color: #fff; border: none; border-radius: 5px; cursor: pointer; transition: background-color 0.3s ease; } button:hover { background-color: #0056b3; } button:disabled { background-color: #ccc; } .is-invalid { border: 1px solid red; } .error-wrapper { color: red; font-size: 12px; margin: 0; padding: 0 20px; } </style>
22
2 комментария

Не мучай себя, возьми нормальный фреймверк типа Laravel / AdonisJS.

В будущем перейду на новый фреймворк, спасибо за совет!