Форматирование чисел и дат

Библиотека Choices.js предоставляет слой над стандартными <select> и <input> элементами, превращая их в управляемые интерфейсные компоненты с поддержкой поиска, тегов и кастомного отображения данных. Отображение значений в интерфейсе не ограничивается строковым выводом: каждый элемент проходит через систему шаблонов и рендер-функций, позволяющих формировать представление данных любой сложности, включая числа и даты.

Внутренне Choices.js оперирует объектами Choice и Item, где ключевыми полями выступают:

  • label — отображаемый текст
  • value — значение, используемое в форме
  • customProperties — дополнительные данные для расширенного рендеринга

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


Подготовка данных для числовых значений

Числовые значения в Choices.js часто используются в сценариях фильтрации: цены, рейтинги, количества, диапазоны. На уровне данных целесообразно хранить «сырое» значение отдельно от отображаемого.

Типовая структура:

const choicesData = [
  {
    value: 'plan_basic',
    label: 'Базовый тариф',
    customProperties: {
      price: 4900
    }
  },
  {
    value: 'plan_pro',
    label: 'Профессиональный тариф',
    customProperties: {
      price: 12900
    }
  }
];

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


Форматирование чисел через Intl.NumberFormat

Для локализованного отображения чисел используется стандартный API Intl.NumberFormat. Он обеспечивает корректное форматирование валют, разделителей тысяч и десятичных знаков.

Пример форматирования цены:

const priceFormatter = new Intl.NumberFormat('ru-RU', {
  style: 'currency',
  currency: 'RUB',
  minimumFractionDigits: 0
});

Функция отображения значения внутри Choices.js:

const formatChoiceLabel = (label, customProperties) => {
  const price = customProperties?.price;

  if (typeof price === 'number') {
    return `${label} — ${priceFormatter.format(price)}`;
  }

  return label;
};

Интеграция форматирования в Choices.js

Choices.js позволяет переопределять шаблоны через callbackOnCreateTemplates, где задаются функции формирования DOM-структур для элементов списка и выбранных значений.

Пример интеграции форматирования чисел:

const choices = new Choices('#select', {
  shouldSort: false,
  callbackOnCreateTemplates: function (template) {
    return {
      item: (classNames, data) => {
        const price = data.customProperties?.price;

        const label = price !== undefined
          ? `${data.label} — ${priceFormatter.format(price)}`
          : data.label;

        return template(`
          <div class="${classNames.item} ${data.highlighted
            ? classNames.highlightedState
            : classNames.itemSelectable}"
            data-item
            data-id="${data.id}"
            data-value="${data.value}">
            ${label}
          </div>
        `);
      }
    };
  }
});

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


Форматирование диапазонов и агрегированных значений

В некоторых интерфейсах Choices.js используется отображение диапазонов цен или агрегированных чисел.

Пример подготовки данных:

{
  value: 'range_1',
  label: 'Эконом',
  customProperties: {
    min: 1000,
    max: 5000
  }
}

Формирование отображения:

const formatRange = (min, max) => {
  return `${priceFormatter.format(min)} – ${priceFormatter.format(max)}`;
};

Использование в шаблоне:

const label = `${data.label} (${formatRange(min, max)})`;

Подобный подход часто применяется в фильтрах интернет-магазинов и аналитических интерфейсах.


Форматирование дат в Choices.js

Работа с датами требует аналогичного разделения данных и отображения. Внутри customProperties обычно хранится ISO-строка или timestamp.

Пример структуры:

{
  value: 'event_1',
  label: 'Запуск продукта',
  customProperties: {
    date: '2026-05-23T10:00:00Z'
  }
}

Использование Intl.DateTimeFormat

Для форматирования дат применяется Intl.DateTimeFormat, обеспечивающий локализацию отображения.

const dateFormatter = new Intl.DateTimeFormat('ru-RU', {
  year: 'numeric',
  month: 'long',
  day: 'numeric'
});

Функция преобразования:

const formatDate = (isoString) => {
  const date = new Date(isoString);
  if (Number.isNaN(date.getTime())) {
    return '';
  }
  return dateFormatter.format(date);
};

Отображение дат в списке Choices.js

Интеграция с шаблоном:

const choices = new Choices('#events', {
  callbackOnCreateTemplates: (template) => ({
    item: (classNames, data) => {
      const date = data.customProperties?.date;
      const formattedDate = date ? formatDate(date) : '';

      return template(`
        <div class="${classNames.item} ${classNames.itemSelectable}"
          data-item
          data-id="${data.id}"
          data-value="${data.value}">
          <span>${data.label}</span>
          ${formattedDate ? `<small>${formattedDate}</small>` : ''}
        </div>
      `);
    }
  })
});

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


Комбинированное форматирование чисел и дат

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

Структура данных:

{
  value: 'tx_1',
  label: 'Оплата заказа',
  customProperties: {
    amount: 2590,
    date: '2026-05-23T08:30:00Z'
  }
}

Формирование отображения:

const formatTransaction = (data) => {
  const amount = data.customProperties?.amount;
  const date = data.customProperties?.date;

  const formattedAmount = typeof amount === 'number'
    ? priceFormatter.format(amount)
    : '';

  const formattedDate = date ? formatDate(date) : '';

  return `
    ${data.label}
    <span class="meta">
      ${formattedAmount}
      ${formattedDate}
    </span>
  `;
};

Локализация и культурные форматы

Форматирование чисел и дат в Choices.js тесно связано с локализацией интерфейса. Использование Intl позволяет избежать жестко заданных форматов и адаптировать представление под разные языки и регионы.

Основные аспекты:

  • разделители тысяч зависят от локали
  • порядок даты (день/месяц/год) варьируется
  • обозначение валюты зависит от стандарта ISO
  • названия месяцев локализуются автоматически

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

const createFormatters = (locale) => ({
  number: new Intl.NumberFormat(locale),
  currency: new Intl.NumberFormat(locale, { style: 'currency', currency: 'USD' }),
  date: new Intl.DateTimeFormat(locale)
});

Обработка ошибок и крайних случаев

При форматировании данных внутри Choices.js важно учитывать некорректные или неполные значения.

Типовые ситуации:

  • null или undefined в customProperties
  • некорректная дата (Invalid Date)
  • строка вместо числа
  • отсутствующее поле валюты

Защитная обработка:

const safeNumberFormat = (value) => {
  if (typeof value !== 'number' || Number.isNaN(value)) {
    return '';
  }
  return priceFormatter.format(value);
};
const safeDateFormat = (value) => {
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) {
    return '';
  }
  return dateFormatter.format(date);
};

Производительность при массовом форматировании

При большом количестве элементов Choices.js (сотни или тысячи значений) повторное создание форматтеров или вычислений внутри рендер-функций приводит к деградации производительности.

Оптимизационные практики:

  • создание Intl.NumberFormat и Intl.DateTimeFormat один раз
  • вынесение форматирующих функций за пределы шаблонов
  • минимизация вычислений внутри callbackOnCreateTemplates
  • предварительное форматирование данных на этапе загрузки

Пример предварительной подготовки:

const preparedData = rawData.map(item => ({
  ...item,
  displayLabel: `${item.label} — ${priceFormatter.format(item.customProperties.price)}`,
  displayDate: formatDate(item.customProperties.date)
}));

Структурирование отображаемых данных

В архитектурно устойчивых решениях Choices.js используется принцип разделения:

  • value — идентификатор
  • label — базовое имя
  • customProperties — сырые данные
  • display* — предрасчитанные строки для UI

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


Кастомизация отображения через CSS

Форматирование часто дополняется стилизацией:

.choices__item small {
  display: block;
  opacity: 0.7;
  font-size: 12px;
}

.choices__item .meta {
  display: flex;
  gap: 8px;
  font-size: 12px;
}

CSS завершает визуальную структуру, сформированную JavaScript-слоем форматирования.