FormData и отправка

Atomico — библиотека для создания веб-компонентов с реактивным подходом, и работа с формами занимает важное место в её экосистеме. Одной из ключевых задач является сбор данных формы и их отправка на сервер. Для этого используется объект FormData, который предоставляет удобный интерфейс для работы с парами «ключ-значение» из HTML-форм.

Создание и инициализация FormData

FormData создаётся на основе HTML-формы или как пустой объект:

const form = document.querySelector('form');
const data = new FormData(form); // Автоматически собирает все input, select, textarea

Можно создавать пустой объект и добавлять пары вручную:

const data = new FormData();
data.append('username', 'ivan');
data.append('password', '12345');

Особенности FormData:

  • Поддерживает текстовые данные и файлы.
  • Автоматически кодирует данные в multipart/form-data.
  • Позволяет добавлять несколько значений под одним ключом с помощью append.

Связывание данных формы с компонентом Atomico

В Atomico формы обычно обрабатываются через обработчики событий, подключенные к элементам формы. Пример компонента:

import { c, useEffect } from "atomico";

function loginForm() {
    const handleSubmit = (event) => {
        event.preventDefault();
        const form = event.target;
        const data = new FormData(form);
        sendData(data);
    };

    return (
        <host>
            <form onSub mit={handleSubmit}>
                <input name="username" type="text" required />
                <input name="password" type="password" required />
                <button type="submit">Войти</button>
            </form>
        </host>
    );
}

loginForm.props = {};

export const LoginForm = c(loginForm);

Здесь event.preventDefault() предотвращает стандартное поведение формы, а FormData собирает все значения элементов с атрибутом name.

Отправка FormData на сервер

Для отправки данных удобно использовать fetch. FormData передаётся напрямую в тело запроса:

async function sendData(formData) {
    try {
        const response = await fetch('/api/login', {
            method: 'POST',
            body: formData
        });
        const result = await response.json();
        console.log(result);
    } catch (error) {
        console.error('Ошибка отправки формы', error);
    }
}

Особенности работы с fetch и FormData:

  • Заголовок Content-Type не нужно указывать вручную — браузер автоматически устанавливает multipart/form-data с boundary.
  • Поддерживаются файлы, Blob и другие бинарные данные.
  • При необходимости можно использовать FormData для отправки JSON, но в этом случае данные сначала нужно сериализовать, что делает FormData менее необходимым.

Работа с файлами

FormData особенно удобна для загрузки файлов. Пример компонента с загрузкой изображения:

function uploadForm() {
    const handleUpload = (event) => {
        event.preventDefault();
        const form = event.target;
        const data = new FormData(form);
        fetch('/api/upload', {
            method: 'POST',
            body: data
        })
        .then(res => res.json())
        .then(console.log)
        .catch(console.error);
    };

    return (
        <host>
            <form onSub mit={handleUpload}>
                <input type="file" name="avatar" />
                <button type="submit">Загрузить</button>
            </form>
        </host>
    );
}

export const UploadForm = c(uploadForm);

Особенности:

  • Несколько файлов можно добавить с одинаковым именем: <input type="file" name="files" multiple />.
  • FormData автоматически хранит каждый файл как отдельный элемент массива при отправке.

Манипуляции с данными перед отправкой

FormData предоставляет методы set, get, delete, has, что позволяет динамически изменять данные перед отправкой:

const data = new FormData();
data.append('username', 'ivan');
data.set('username', 'petr'); // заменяет значение
data.has('username'); // true
data.delete('username'); // удаляет ключ

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

Интеграция с реактивными состояниями Atomico

В Atomico можно использовать useState для отслеживания состояния формы и динамического обновления FormData:

import { c, useState } from "atomico";

function reactiveForm() {
    const [formData, setFormData] = useState(new FormData());

    const handleChange = (event) => {
        const { name, value } = event.target;
        formData.set(name, value);
        setFormData(formData);
    };

    const handleSubmit = (event) => {
        event.preventDefault();
        fetch('/api/submit', { method: 'POST', body: formData });
    };

    return (
        <host>
            <form onSub mit={handleSubmit}>
                <input name="firstName" onIn put={handleChange} />
                <input name="lastName" onIn put={handleChange} />
                <button type="submit">Отправить</button>
            </form>
        </host>
    );
}

export const ReactiveForm = c(reactiveForm);

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

Советы по оптимизации

  • Использовать FormData напрямую при работе с файлами, а для простых текстовых данных может быть удобнее JSON.
  • Для больших форм и множественных файлов стоит отслеживать размер данных и показывать индикатор загрузки.
  • Для повторного использования полей формы внутри нескольких компонентов можно создавать универсальные функции сборки FormData.

FormData в сочетании с Atomico позволяет создавать реактивные и динамические формы, легко интегрируемые с серверной частью, с минимальной нагрузкой на код и максимально чистым интерфейсом.