Отправка данных форм

Cleave.js предназначена для форматирования пользовательского ввода, но в контексте отправки форм ключевой задачей становится разделение визуального представления данных и их фактического значения, которое уходит на сервер. Основная сложность возникает из-за того, что отображаемое значение (с пробелами, разделителями, масками) не всегда совпадает с «чистыми» данными, необходимыми для обработки на backend.

При работе с форматированными полями важно учитывать, что Cleave.js хранит два состояния:

  • formatted value — значение, которое видит пользователь;
  • raw value — «чистое» значение без форматирования.

Именно raw value используется при отправке формы, поскольку он не содержит символов группировки, разделителей дат или масок телефонов.

В типичном сценарии обработка строится вокруг свойства экземпляра Cleave:

const cleave = new Cleave(input, {
    phone: true,
    phoneRegionCode: 'RU'
});

console.log(cleave.getRawValue());
console.log(cleave.getFormattedValue());

Отправка формы должна опираться исключительно на getRawValue().

Базовая интеграция с HTML-формой

При стандартной отправке через <form> возникает проблема: браузер отправляет именно значение input, которое уже отформатировано. Поэтому требуется дополнительный слой синхронизации.

Наиболее распространённый подход — использование скрытого поля:

<form id="userForm">
    <input id="phoneInput" type="text">
    <input id="phoneHidden" type="hidden" name="phone">
    <button type="submit">Отправить</button>
</form>

И синхронизация через события Cleave.js:

const phoneInput = document.getElementById('phoneInput');
const hiddenInput = document.getElementById('phoneHidden');

const cleavePhone = new Cleave(phoneInput, {
    phone: true,
    phoneRegionCode: 'RU',
    onValueChanged: function(e) {
        hiddenInput.value = e.target.rawValue;
    }
});

Такой подход гарантирует, что сервер получает только нормализованное значение.

Использование события onValueChanged

Ключевой механизм интеграции с формами — событие onValueChanged. Оно вызывается при каждом изменении поля и предоставляет доступ к структуре:

  • value — текущее отображаемое значение;
  • rawValue — очищенное значение;
  • target — DOM-элемент.

Пример обработки:

onValueChanged: function(e) {
    const { rawValue, value } = e.target;

    console.log('UI:', value);
    console.log('SERVER:', rawValue);
}

Для отправки формы важно фиксировать именно rawValue, так как он устойчив к форматированию и локализации.

Отправка данных через fetch

При использовании программной отправки формы (AJAX) необходимость скрытых полей исчезает, поскольку можно напрямую извлечь rawValue.

const form = document.getElementById('userForm');

form.addEventListener('submit', async (e) => {
    e.preventDefault();

    const payload = {
        phone: cleavePhone.getRawValue()
    };

    await fetch('/api/submit', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(payload)
    });
});

В этом случае Cleave.js выступает исключительно как слой UX-форматирования, не влияющий на структуру данных запроса.

Отправка через FormData

Если требуется сохранить нативную модель отправки формы, используется FormData, но с ручной заменой значений:

form.addEventListener('submit', (e) => {
    e.preventDefault();

    const formData = new FormData(form);

    formData.set('phone', cleavePhone.getRawValue());

    fetch('/api/submit', {
        method: 'POST',
        body: formData
    });
});

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

Работа с несколькими форматированными полями

В реальных интерфейсах часто присутствует несколько экземпляров Cleave.js: телефон, карта, дата. В таком случае вводится единый слой агрегации данных.

const cleavePhone = new Cleave(phoneInput, { phone: true });
const cleaveCard = new Cleave(cardInput, { creditCard: true });
const cleaveDate = new Cleave(dateInput, { date: true, datePattern: ['d', 'm', 'Y'] });

function collectData() {
    return {
        phone: cleavePhone.getRawValue(),
        card: cleaveCard.getRawValue(),
        date: cleaveDate.getRawValue()
    };
}

Такой подход исключает зависимость от DOM-значений и делает слой отправки данных предсказуемым.

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

Отдельный слой логики часто строится вокруг проверки rawValue. Например, телефон может быть визуально корректным, но неполным.

function isPhoneValid(cleaveInstance) {
    return cleaveInstance.getRawValue().length >= 10;
}

form.addEventListener('submit', (e) => {
    e.preventDefault();

    if (!isPhoneValid(cleavePhone)) {
        return;
    }

    sendData();
});

Важно, что проверка проводится не по value, а по очищенному значению, иначе форматирование может исказить результат.

Динамическое изменение формата и отправка

Cleave.js позволяет менять конфигурацию на лету, что влияет и на стратегию отправки данных. При изменении типа поля (например, переключение между международным и локальным форматом телефона) необходимо учитывать, что raw значение может менять структуру.

cleavePhone.setPhoneRegionCode('US');

После таких изменений backend должен получать только актуализированное значение getRawValue().

Интеграция с React-подобной моделью состояния

При использовании state-based подхода важно синхронизировать Cleave.js с состоянием приложения:

let state = {
    phone: ''
};

new Cleave(input, {
    phone: true,
    onValueChanged: function(e) {
        state.phone = e.target.rawValue;
    }
});

Отправка затем полностью независима от DOM:

async function submit() {
    await fetch('/api/submit', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(state)
    });
}

Такой подход устраняет необходимость обращения к input-элементам во время отправки.

Ошибки при отправке данных

Типичные проблемы возникают при неправильной интерпретации значений:

  • отправка formatted value вместо raw value;
  • дублирование скрытых полей и JS-значений;
  • отсутствие синхронизации при программном изменении input;
  • попытка сериализации DOM-значения без учёта маски.

Особенно критично это для финансовых и идентификационных данных, где символы форматирования могут полностью изменить смысл строки.

Сценарий серверной нормализации vs клиентской

Иногда возникает вопрос: очищать ли данные на клиенте или доверить это серверу. При использовании Cleave.js логика обычно смещается в сторону клиентской нормализации, поскольку библиотека уже предоставляет гарантированно «чистое» значение через API.

Однако даже в этом случае серверная валидация остаётся обязательной, так как форматирование не является защитным механизмом.

Поведение при reset формы

При сбросе формы через form.reset() Cleave.js не всегда автоматически синхронизирует внутреннее состояние. Поэтому требуется дополнительный вызов:

form.addEventListener('reset', () => {
    cleavePhone.setRawValue('');
});

Без этого возможно расхождение между визуальным состоянием и данными экземпляра.

Обработка отправки в сложных UI

В интерфейсах с динамическими формами (модальные окна, шаговые формы) экземпляры Cleave.js часто создаются и уничтожаются. В таких сценариях отправка данных строится вокруг централизованного менеджера:

const instances = new Map();

instances.set('phone', cleavePhone);
instances.set('card', cleaveCard);

function buildPayload() {
    return {
        phone: instances.get('phone').getRawValue(),
        card: instances.get('card').getRawValue()
    };
}

Такой слой предотвращает потерю состояния при размонтировании компонентов.

Итоговая модель потока данных

Общая схема отправки данных при использовании Cleave.js выглядит как последовательность:

  1. Пользователь вводит данные в форматированное поле.
  2. Cleave.js преобразует ввод в визуально удобный формат.
  3. Внутренне формируется raw value.
  4. При событии изменения значение синхронизируется с состоянием или скрытым полем.
  5. При отправке формы используется только raw value.
  6. Данные уходят на сервер без форматирующих символов.

Такая архитектура обеспечивает стабильное разделение UI-слоя и слоя данных, сохраняя предсказуемость обработки ввода на стороне сервера.