Валидация форм является важной частью фронтенд-разработки, которая помогает улучшить пользовательский опыт и предотвратить ошибки при отправке данных на сервер. В этой статье мы рассмотрим, как использовать библиотеку 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
Установим необходимые зависимости в нашем проекте:
npm install ajv ajv-formats ajv-errors
Создание файла схемы валидации login.json
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()
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()
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 для валидации формы. Это позволяет создавать мощные и гибкие системы валидации, которые помогут улучшить пользовательский опыт и обеспечить корректную обработку данных на сервере.
Исходный код
App.vue
<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>
shasoftX
Странно что правило required вынесено отдельно. Т.е. было бы логично вот так определить правила
glebanya13 Автор
Правила/схему валидации создаем мы, но формат не наш, это много лет существующая спецификация Json-Schema https://json-schema.org/specification-links.html