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

Haunted — это библиотека для создания веб-компонентов с использованием реактивного подхода, вдохновлённого функциональными компонентами React. Валидация форм в Haunted строится на реактивных сигналах и управлении состоянием компонентов через useState и useEffect, что позволяет эффективно отслеживать изменения данных и динамически обновлять интерфейс.

Управление состоянием формы

Форма в Haunted обычно представлена реактивным объектом состояния, который хранит значения полей и информацию о корректности данных:

import { html, component } from 'haunted';
import { useState, useEffect } from 'haunted';

function FormComponent() {
  const [formData, setFormData] = useState({
    username: '',
    email: '',
    password: ''
  });

  const [errors, setErrors] = useState({});
  
  return html`
    <form @submit=${handleSubmit}>
      <input
        type="text"
        name="username"
        .value=${formData.username}
        @input=${e => setFormData({...formData, username: e.target.value})}
      />
      ${errors.username ? html`<span>${errors.username}</span>` : ''}
      
      <input
        type="email"
        name="email"
        .value=${formData.email}
        @input=${e => setFormData({...formData, email: e.target.value})}
      />
      ${errors.email ? html`<span>${errors.email}</span>` : ''}
      
      <input
        type="password"
        name="password"
        .value=${formData.password}
        @input=${e => setFormData({...formData, password: e.target.value})}
      />
      ${errors.password ? html`<span>${errors.password}</span>` : ''}
      
      <button type="submit">Отправить</button>
    </form>
  `;

  function handleSubmit(e) {
    e.preventDefault();
    const validationErrors = validate(formData);
    setErrors(validationErrors);

    if (Object.keys(validationErrors).length === 0) {
      console.log('Форма корректна', formData);
    }
  }
}

customElements.define('form-component', component(FormComponent));

В этом примере состояние formData содержит значения полей, а errors хранит ошибки валидации. Реактивность Haunted позволяет автоматически перерисовывать интерфейс при изменении этих объектов.

Функция валидации

Валидация реализуется отдельной функцией, которая возвращает объект с ошибками:

function validate(data) {
  const errors = {};

  if (!data.username.trim()) {
    errors.username = 'Имя пользователя обязательно';
  } else if (data.username.length < 3) {
    errors.username = 'Имя пользователя должно быть не менее 3 символов';
  }

  if (!data.email.trim()) {
    errors.email = 'Email обязателен';
  } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) {
    errors.email = 'Неверный формат email';
  }

  if (!data.password) {
    errors.password = 'Пароль обязателен';
  } else if (data.password.length < 6) {
    errors.password = 'Пароль должен быть не менее 6 символов';
  }

  return errors;
}

Такой подход отделяет бизнес-логику проверки от интерфейса, что упрощает тестирование и поддержку кода.

Динамическая реакция на ввод

Haunted позволяет реагировать на изменения данных в реальном времени с помощью useEffect. Например, можно автоматически проверять поле при изменении значения:

useEffect(() => {
  setErrors(prevErrors => {
    const newErrors = {...prevErrors};
    if (formData.username && formData.username.length >= 3) {
      delete newErrors.username;
    }
    return newErrors;
  });
}, [formData.username]);

Подобная реактивная проверка позволяет пользователю видеть ошибки сразу, не дожидаясь отправки формы.

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

Для сложных форм удобно создавать отдельные валидаторы для каждого поля:

const validators = {
  username: value => value.length >= 3 ? null : 'Имя слишком короткое',
  email: value => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) ? null : 'Неверный формат email',
  password: value => value.length >= 6 ? null : 'Пароль слишком короткий'
};

function validateField(name, value) {
  return validators[name] ? validators[name](value) : null;
}

function handleInputChange(e) {
  const { name, value } = e.target;
  setFormData({...formData, [name]: value});
  setErrors({...errors, [name]: validateField(name, value)});
}

Такой подход упрощает добавление новых полей и изменяемых правил валидации без переписывания основной логики.

Валидация перед отправкой

Даже при динамической проверке важно проводить финальную проверку при сабмите формы:

function handleSubmit(e) {
  e.preventDefault();
  const newErrors = Object.fromEntries(
    Object.entries(formData).map(([key, value]) => [key, validateField(key, value)]).filter(([_, v]) => v)
  );

  setErrors(newErrors);

  if (!Object.keys(newErrors).length) {
    // отправка данных на сервер
  }
}

Использование реактивных сигналов Haunted вместе с функциями проверки позволяет минимизировать дублирование кода и гарантировать консистентность состояния формы.

Поддержка сложных структур данных

Форма может содержать вложенные объекты, массивы элементов или динамически создаваемые поля. Haunted позволяет управлять такими структурами через useState и обновление по ссылке:

const [addresses, setAddresses] = useState([{ street: '', city: '' }]);

function addAddress() {
  setAddresses([...addresses, { street: '', city: '' }]);
}

function updateAddress(index, field, value) {
  const updated = addresses.map((addr, i) => i === index ? {...addr, [field]: value} : addr);
  setAddresses(updated);
}

Валидация массивов и вложенных объектов реализуется аналогично: перебор элементов с применением валидаторов к каждому полю.

Интеграция с кастомными элементами

Haunted прекрасно работает с кастомными элементами <my-input> или сторонними компонентами. Для этого достаточно привязывать события @input и значения через .value или свойства, что позволяет строить гибкие формы с полной реактивностью.


Эти подходы делают Haunted удобным инструментом для построения сложных форм с полной поддержкой реактивной валидации, мгновенной обратной связью и управляемым состоянием.