Формы и валидация

Формы в SvelteKit строятся на стандартных HTML-элементах, но библиотека предоставляет расширенные возможности через реактивные переменные и специализированные UI-библиотеки. Основной принцип работы заключается в связке состояния формы с компонентом.

Пример базовой формы с реактивными переменными:

<script>
  let name = '';
  let email = '';
  let message = '';
  
  function handleSubmit() {
    console.log({ name, email, message });
  }
</script>

<form on:submit|preventDefault={handleSubmit}>
  <label>
    Имя
    <input type="text" bind:value={name} required />
  </label>
  
  <label>
    Email
    <input type="email" bind:value={email} required />
  </label>
  
  <label>
    Сообщение
    <textarea bind:value={message}></textarea>
  </label>
  
  <button type="submit">Отправить</button>
</form>

В этом примере bind:value автоматически синхронизирует значение поля с переменной компонента, что обеспечивает двустороннюю привязку данных.

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

Валидация делится на два уровня: клиентская и серверная.

Клиентская валидация

Клиентская валидация может выполняться как нативными средствами HTML (required, type="email"), так и с использованием реактивных выражений и библиотек вроде svelte-forms-lib или felte.

Пример реактивной проверки:

<script>
  let email = '';
  $: isValidEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
</script>

<input type="email" bind:value={email} placeholder="Введите email" />
{#if email && !isValidEmail}
  <p style="color:red;">Неверный формат email</p>
{/if}

Здесь реактивная переменная $: isValidEmail отслеживает изменения email и динамически показывает сообщение об ошибке.

Серверная валидация

SvelteKit поддерживает обработку форм через actions в файлах +page.server.js или +page.server.ts.

export const actions = {
  default: async ({ request }) => {
    const data = Object.fromEntries(await request.formData());
    const errors = {};
    
    if (!data.name) errors.name = 'Имя обязательно';
    if (!data.email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) errors.email = 'Неверный email';
    
    if (Object.keys(errors).length) {
      return { success: false, errors };
    }
    
    return { success: true, data };
  }
};

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

Интеграция с UI-библиотеками

SvelteKit совместим с несколькими популярными UI-библиотеками, которые упрощают создание форм и валидацию:

  • Svelte Material UI (SMUI) — предоставляет компоненты TextField, Checkbox, Select с поддержкой валидации и стилями Material Design.
  • Skeleton UI — современная библиотека с готовыми формами, интегрированными с реактивными состояниями Svelte.
  • Felte — библиотека для декларативного управления формами с поддержкой валидации на основе схем (yup, zod).

Пример интеграции Felte с SvelteKit:

<script>
  import { createForm } from 'felte';
  import { validator } from '@felte/validator-yup';
  import * as yup from 'yup';

  const schema = yup.object({
    name: yup.string().required('Имя обязательно'),
    email: yup.string().email('Неверный email').required('Email обязателен')
  });

  const { form, errors } = createForm({
    initialValues: { name: '', email: '' },
    onSubmit: values => console.log(values),
    extend: validator({ schema })
  });
</script>

<form use:form>
  <label>
    Имя
    <input name="name" />
    {#if errors.name}<p>{errors.name}</p>{/if}
  </label>
  
  <label>
    Email
    <input name="email" />
    {#if errors.email}<p>{errors.email}</p>{/if}
  </label>
  
  <button type="submit">Отправить</button>
</form>

Такой подход позволяет объединять двустороннюю привязку, декларативную валидацию и пользовательский интерфейс в одном компоненте.

Обработка сложных форм

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

Пример динамического списка контактов:

<script>
  let contacts = [{ type: 'email', value: '' }];
  
  function addContact() {
    contacts = [...contacts, { type: 'email', value: '' }];
  }
  
  function removeContact(index) {
    contacts = contacts.filter((_, i) => i !== index);
  }
</script>

{#each contacts as contact, i}
  <div>
    <input type="text" bind:value={contact.value} placeholder="Контакт" />
    <button type="button" on:click={() => removeContact(i)}>Удалить</button>
  </div>
{/each}

<button type="button" on:click={addContact}>Добавить контакт</button>

Комбинация динамических форм и валидации через Felte или реактивные выражения позволяет создавать интерактивные, масштабируемые формы без потери контроля над состоянием и ошибками.

Стилизация и UX

Правильная стилизация форм важна для восприятия и удобства:

  • Использовать индикаторы ошибок рядом с полями.
  • Обеспечивать реактивную подсказку при вводе.
  • Добавлять кнопки очистки и динамическое добавление полей, чтобы улучшить интерактивность.
  • Поддерживать доступность (ARIA) через атрибуты aria-invalid, aria-describedby.

SvelteKit и современные UI-библиотеки позволяют реализовать эти элементы максимально декларативно и реактивно, избегая излишней сложности кода.

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