Множественные поля в одной форме

Работа с несколькими числовыми полями в одной форме в AutoNumeric требует понимания того, что каждая инстанция библиотеки существует независимо, но при этом все они обычно участвуют в едином процессе сбора и валидации данных формы. Основная сложность заключается не в самой инициализации форматирования, а в согласованной работе с DOM-состоянием, извлечении «чистых» значений и управлении динамическими полями.

Каждое поле ввода, к которому применяется AutoNumeric, превращается в отдельный объект-обёртку с собственными настройками форматирования, локализации и состояния курсора. При работе с формой это означает:

  • каждое поле хранит отформатированное значение отдельно
  • внутреннее числовое значение не зависит от визуального представления
  • доступ к данным всегда осуществляется через API библиотеки, а не через input.value

При инициализации нескольких полей создаётся набор независимых экземпляров:

const amount = new AutoNumeric('#amount', {
  digitGroupSeparator: ' ',
  decimalCharacter: ',',
  decimalPlaces: 2
});

const tax = new AutoNumeric('#tax', {
  digitGroupSeparator: ' ',
  decimalCharacter: ',',
  decimalPlaces: 2
});

const discount = new AutoNumeric('#discount', {
  digitGroupSeparator: ' ',
  decimalCharacter: ',',
  decimalPlaces: 2
});

Каждый экземпляр изолирован, но в контексте формы они начинают взаимодействовать косвенно — через вычисления, события и сбор данных.

Единая стратегия инициализации

При большом количестве полей ручное создание экземпляров становится неэффективным. Типичный подход — делегирование инициализации через атрибуты:

<form id="invoiceForm">
  <input id="amount" data-an />
  <input id="tax" data-an />
  <input id="discount" data-an />
  <input id="total" data-an-readonly />
</form>

Инициализация всех полей одной операцией:

const elements = document.querySelectorAll('[data-an], [data-an-readonly]');

const instances = [];

elements.forEach((el) => {
  const isReadonly = el.hasAttribute('data-an-readonly');

  const instance = new AutoNumeric(el, {
    decimalCharacter: ',',
    digitGroupSeparator: ' ',
    decimalPlaces: 2,
    readOnly: isReadonly
  });

  instances.push(instance);
});

Такой подход создаёт централизованный список инстансов, что критично для дальнейшей синхронизации и массового извлечения данных.

Получение значений всех полей формы

Ключевой момент при работе с множественными полями — отказ от использования FormData как единственного источника числовых значений. AutoNumeric хранит отформатированное значение в DOM, поэтому прямое чтение value приводит к ошибкам.

Правильный подход — извлечение числовых значений через API:

const formData = {};

instances.forEach((instance) => {
  const el = instance.domElement;
  const name = el.id || el.name;

  formData[name] = instance.getNumber();
});

Метод getNumber() возвращает нормализованное значение без разделителей групп и с учётом настроек точности.

Для сериализации перед отправкой:

const payload = JSON.stringify(formData);

Синхронизация полей внутри формы

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

Пример зависимости:

  • amount — базовая сумма
  • tax — процент налога
  • discount — скидка
  • total — вычисляемый результат
function recalculate() {
  const amountVal = amount.getNumber();
  const taxVal = tax.getNumber();
  const discountVal = discount.getNumber();

  const totalValue = amountVal + (amountVal * taxVal / 100) - discountVal;

  total.set(totalValue);
}

Здесь важно, что метод set() принимает числовое значение, а AutoNumeric сам приводит его к нужному формату отображения.

Обработка событий в нескольких полях

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

instances.forEach((instance) => {
  instance.domElement.addEventListener('autoNumeric:formatted', recalculate);
  instance.domElement.addEventListener('input', recalculate);
});

Событие autoNumeric:formatted позволяет реагировать уже на завершённое форматирование, что уменьшает количество промежуточных пересчётов.

В более сложных формах применяется throttling:

let timeout;

function throttledRecalculate() {
  clearTimeout(timeout);
  timeout = setTimeout(recalculate, 50);
}

Динамическое добавление полей

Формы часто позволяют добавлять строки (например, позиции счёта). В этом случае каждый новый input требует отдельной инициализации AutoNumeric.

function addRow() {
  const container = document.querySelector('#items');

  const input = document.createElement('input');
  input.type = 'text';
  input.className = 'price';

  container.appendChild(input);

  const instance = new AutoNumeric(input, {
    decimalCharacter: ',',
    digitGroupSeparator: ' ',
    decimalPlaces: 2
  });

  instances.push(instance);
}

Важно поддерживать актуальный массив instances, иначе сбор данных формы станет неконсистентным.

Удаление и уничтожение инстансов

При удалении строк необходимо корректно освобождать ресурсы:

function removeRow(index) {
  const instance = instances[index];

  instance.remove(); // или destroy в зависимости от версии

  instances.splice(index, 1);
}

Игнорирование удаления приводит к накоплению «мертвых» ссылок и некорректной работе пересчётов.

Разделение логики отображения и данных

Одним из ключевых аспектов при работе с несколькими полями является строгая граница между UI и данными:

  • UI-слой: AutoNumeric форматирует ввод
  • Data-слой: хранит числовые значения через getNumber()

Пример неправильного подхода:

const value = document.querySelector('#amount').value;

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

Корректный вариант:

const value = amount.getNumber();

Массовое обновление значений

В некоторых сценариях требуется обновить сразу несколько полей (например, смена валюты или коэффициента пересчёта):

function applyMultiplier(multiplier) {
  instances.forEach((instance) => {
    const value = instance.getNumber();
    instance.set(value * multiplier);
  });
}

AutoNumeric автоматически перерисовывает отображение при каждом set(), сохраняя формат.

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

При наличии множества полей возникает задача контроля валидности всей формы:

function isFormValid() {
  return instances.every((instance) => {
    const value = instance.getNumber();
    return value !== null && !isNaN(value);
  });
}

Дополнительно можно учитывать ограничения конфигурации:

  • минимальные значения
  • максимальные значения
  • разрешённые диапазоны

Оптимизация работы при большом количестве полей

При сотнях инстансов важны следующие принципы:

  • минимизация пересчётов (batch updates)
  • использование делегированных событий
  • отказ от частого чтения DOM
  • кеширование ссылок на инстансы

Пример пакетного обновления:

function batchUpdate(updates) {
  updates.forEach(({ instance, value }) => {
    instance.set(value);
  });
}

После завершения всех операций выполняется один пересчёт зависимых значений.

Согласованность локализации

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

const commonOptions = {
  digitGroupSeparator: ' ',
  decimalCharacter: ',',
  decimalPlaces: 2
};

instances.forEach((instance) => {
  instance.update(commonOptions);
});

Изменение локали «на лету» требует обновления всех инстансов, иначе пользовательский ввод становится неоднородным.

Работа с формой при отправке

Перед отправкой формы важно гарантировать, что данные извлекаются из всех инстансов:

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

  const result = instances.reduce((acc, instance) => {
    const key = instance.domElement.name;
    acc[key] = instance.getNumber();
    return acc;
  }, {});

  send(result);
});

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