Валидация входных данных в JavaScript-проектах с использованием
Validator.js чаще всего строится вокруг идеи явного накопления ошибок.
Библиотека предоставляет набор чистых функций, возвращающих
true или false, поэтому механизм ошибок
полностью реализуется на стороне приложения.
Типовая модель хранения ошибок представляет собой объект, где ключи соответствуют именам полей, а значения — массивы сообщений:
const errors = {
email: ["Некорректный email адрес"],
password: ["Пароль слишком короткий", "Пароль должен содержать цифры"]
};
Такой подход позволяет одновременно сохранять несколько нарушений для одного поля, что особенно важно при сложных правилах валидации.
При использовании Validator.js логика проверки обычно выстраивается последовательно, с явным добавлением сообщений при несоответствии условиям:
import validator from "validator";
function validateUser(data) {
const errors = {};
if (!validator.isEmail(data.email || "")) {
errors.email = errors.email || [];
errors.email.push("Email не соответствует формату");
}
if (!validator.isLength(data.password || "", { min: 8 })) {
errors.password = errors.password || [];
errors.password.push("Минимальная длина пароля — 8 символов");
}
return errors;
}
Подобная структура обеспечивает предсказуемость результата и упрощает дальнейшее отображение ошибок в интерфейсе.
На уровне отображения данные ошибок часто преобразуются в более удобный формат. Например, массив сообщений может быть сведен к одной строке или первой ошибке:
function normalizeErrors(errors) {
const result = {};
Object.keys(errors).forEach((field) => {
result[field] = errors[field][0];
});
return result;
}
Такой вариант используется в формах, где важно показать только одно сообщение на поле, без перегрузки интерфейса.
Альтернативный подход — объединение всех сообщений в одну строку:
function joinErrors(errors) {
const result = {};
Object.keys(errors).forEach((field) => {
result[field] = errors[field].join(". ");
});
return result;
}
На стороне интерфейса ошибки обычно связываются с конкретными полями формы. В DOM-структурах это реализуется через идентификаторы или data-атрибуты:
function renderErrors(errors) {
Object.keys(errors).forEach((field) => {
const errorContainer = document.querySelector(`[data-error="${field}"]`);
if (errorContainer) {
errorContainer.textContent = errors[field][0];
}
});
}
При таком подходе логика отображения отделяется от логики валидации, что упрощает сопровождение кода.
Перед повторным запуском проверки важно сбрасывать предыдущие сообщения, иначе интерфейс будет содержать устаревшие данные:
function clearErrors() {
document.querySelectorAll("[data-error]").forEach((el) => {
el.textContent = "";
});
}
На уровне структуры данных аналогично создаётся новый объект ошибок или очищаются существующие поля:
function resetErrors(errors) {
Object.keys(errors).forEach((key) => delete errors[key]);
}
Ошибки не всегда отображаются сразу после первой проверки. Распространённый подход — показывать их только после попытки отправки формы:
let touched = false;
function handleSubmit(data) {
touched = true;
const errors = validateUser(data);
if (Object.keys(errors).length > 0) {
renderErrors(errors);
return;
}
submitForm(data);
}
Дополнительно может использоваться проверка touched для
каждого поля, что позволяет отображать ошибки только после
взаимодействия с ним.
При множественных проверках одного поля важно управлять порядком сообщений. Например, сначала проверяется обязательность поля, затем формат:
if (validator.isEmpty(data.email || "")) {
errors.email = ["Email обязателен"];
} else if (!validator.isEmail(data.email)) {
errors.email = ["Некорректный формат email"];
}
Такой подход предотвращает накопление избыточных сообщений и делает вывод более логичным.
Некоторые проверки требуют обращения к серверу, например, проверка уникальности email. В таких случаях ошибки добавляются асинхронно:
async function validateEmailUniqueness(email, errors) {
const exists = await api.checkEmail(email);
if (exists) {
errors.email = errors.email || [];
errors.email.push("Email уже используется");
}
}
При отображении таких ошибок важно учитывать состояние загрузки, чтобы интерфейс не показывал промежуточные результаты.
В формах с вложенными объектами (например, адреса или профили) ошибки часто повторяют структуру данных:
const errors = {
user: {
name: ["Имя обязательно"],
address: {
city: ["Город не указан"]
}
}
};
Для отображения таких структур используется рекурсивный обход:
function renderNestedErrors(errors, prefix = "") {
Object.keys(errors).forEach((key) => {
const value = errors[key];
if (Array.isArray(value)) {
const el = document.querySelector(`[data-error="${prefix}${key}"]`);
if (el) el.textContent = value[0];
} else {
renderNestedErrors(value, `${prefix}${key}.`);
}
});
}
Для обеспечения единообразия интерфейса часто применяется централизованный словарь сообщений:
const messages = {
required: "Поле обязательно для заполнения",
email: "Некорректный email",
minLength: (min) => `Минимальная длина ${min} символов`
};
При формировании ошибок используются шаблоны:
if (validator.isEmpty(password)) {
errors.password = [messages.required];
}
if (!validator.isLength(password, { min: 8 })) {
errors.password.push(messages.minLength(8));
}
В архитектуре клиент-сервер ошибки проходят несколько этапов преобразования:
Пример серверного ответа:
{
"errors": {
"email": ["Email уже зарегистрирован"]
}
}
Фронтенд адаптирует структуру без изменения семантики сообщений.
В крупных приложениях ошибки часто становятся частью переиспользуемых модулей. Это позволяет унифицировать поведение форм:
function createError(field, message) {
return {
field,
message,
timestamp: Date.now()
};
}
Такая модель упрощает логирование и отладку, особенно при сложных сценариях валидации.
В интерактивных формах ошибки могут пересчитываться при каждом изменении значения поля:
function handleChange(field, value) {
const errors = validateField(field, value);
renderErrors(errors);
}
Это позволяет реализовать мгновенную обратную связь без ожидания отправки формы.
При значительном числе ошибок важно избегать перегрузки интерфейса. Используются стратегии:
Такая фильтрация снижает когнитивную нагрузку и улучшает читаемость интерфейса.
В современных SPA-приложениях ошибки часто хранятся в состоянии компонента или глобальном сторе:
const state = {
errors: {}
};
Изменение состояния автоматически приводит к обновлению интерфейса, что упрощает синхронизацию данных и отображения.