Формирование отображаемых значений в интерфейсе выбора в Tom Select напрямую влияет на восприятие данных пользователем, особенно когда элементы содержат числовые значения, валюты, даты, временные метки или смешанные типы данных. Библиотека оперирует строковыми представлениями, однако источником данных часто выступают структурированные объекты, требующие предварительного или динамического форматирования на уровне рендера.
Внутренний механизм Tom Select строится вокруг объектов вида:
{
value: 1,
text: "2024-01-01"
}
или более сложных структур:
{
id: 42,
name: "Order",
amount: 15000,
createdAt: "2025-05-10T14:30:00Z"
}
По умолчанию библиотека использует строковое поле text,
однако при работе с числовыми и временными данными этого недостаточно.
Основная задача заключается в том, чтобы преобразование происходило либо
на этапе подготовки данных, либо на этапе рендера элементов.
Первый подход — нормализация данных до их передачи в компонент. Это снижает вычислительную нагрузку при рендеринге.
const data = orders.map(order => ({
value: order.id,
text: new Intl.NumberFormat('ru-RU').format(order.amount)
}));
Использование Intl.NumberFormat позволяет учитывать
локаль, разделители тысяч и формат отображения валютных значений.
Расширенный вариант с валютой:
const formatter = new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: 'KZT'
});
const data = orders.map(order => ({
value: order.id,
text: formatter.format(order.amount)
}));
Такой подход обеспечивает единообразное отображение независимо от источника данных.
При необходимости сохранить исходные значения и выполнять
форматирование только при отображении применяется переопределение
render:
new TomSelect("#select", {
options: orders,
valueField: "id",
labelField: "name",
searchField: ["name"],
render: {
option: function(data, escape) {
return `
<div>
<span>${escape(data.name)}</span>
<small>${new Intl.NumberFormat('ru-RU').format(data.amount)}</small>
</div>
`;
},
item: function(data, escape) {
return `<div>${escape(data.name)} — ${new Intl.NumberFormat('ru-RU').format(data.amount)}</div>`;
}
}
});
Здесь форматирование отделено от модели данных, что позволяет изменять отображение без трансформации исходных объектов.
Работа с датами требует отдельного подхода, так как строковое представление ISO 8601 не подходит для интерфейсов пользователя.
Базовое преобразование:
const data = events.map(event => ({
value: event.id,
text: new Date(event.date).toLocaleDateString('ru-RU')
}));
Более контролируемый вариант через
Intl.DateTimeFormat:
const dateFormatter = new Intl.DateTimeFormat('ru-RU', {
year: 'numeric',
month: 'long',
day: '2-digit'
});
const data = events.map(event => ({
value: event.id,
text: dateFormatter.format(new Date(event.date))
}));
Такой способ обеспечивает стабильность форматирования при изменении локали и предотвращает неоднозначность представления даты.
При работе с бизнес-данными часто требуется отображение нескольких типов значений одновременно:
new TomSelect("#select", {
options: invoices,
valueField: "id",
labelField: "title",
searchField: ["title"],
render: {
option: function(data, escape) {
const amount = new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: 'KZT'
}).format(data.amount);
const date = new Intl.DateTimeFormat('ru-RU', {
year: 'numeric',
month: 'short',
day: '2-digit'
}).format(new Date(data.createdAt));
return `
<div>
<div>${escape(data.title)}</div>
<div style="opacity:0.7; font-size:12px">
${amount} • ${date}
</div>
</div>
`;
}
}
});
Такой подход формирует многоуровневое представление данных без изменения их структуры.
При большом объёме элементов (тысячи записей) форматирование на этапе рендера может стать узким местом. В таких случаях применяется предрасчёт:
const prepared = transactions.map(t => {
const amount = new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: 'KZT'
}).format(t.amount);
const date = new Date(t.date).toLocaleDateString('ru-RU');
return {
id: t.id,
title: t.title,
amountFormatted: amount,
dateFormatted: date
};
});
И затем используется уже готовое поле:
render: {
option: function(data, escape) {
return `
<div>
<div>${escape(data.title)}</div>
<small>${data.amountFormatted} • ${data.dateFormatted}</small>
</div>
`;
}
}
Tom Select использует searchField, который работает по
строковым значениям. При числовых данных требуется явное приведение:
new TomSelect("#select", {
options: products,
valueField: "id",
labelField: "title",
searchField: ["title", "amountString"],
render: {
option: function(data, escape) {
return `
<div>
${escape(data.title)}
<small>${data.amountString}</small>
</div>
`;
}
}
});
Подготовка данных:
const products = rawProducts.map(p => ({
...p,
amountString: String(p.amount)
}));
Для более точного поиска используется нормализация без форматирования (без пробелов и символов валюты), чтобы избежать искажений поиска.
При динамическом изменении языка интерфейса форматирование должно быть реактивным. Используется фабрика форматтеров:
function createFormatters(locale) {
return {
number: new Intl.NumberFormat(locale),
currency: new Intl.NumberFormat(locale, {
style: "currency",
currency: "KZT"
}),
date: new Intl.DateTimeFormat(locale)
};
}
И применение в рендере:
const fmt = createFormatters('ru-RU');
render: {
option: function(data, escape) {
return `
<div>
${escape(data.name)}
<small>${fmt.currency.format(data.amount)}</small>
</div>
`;
}
}
Частая ошибка заключается в смешивании логики отображения и логики
хранения. Tom Select хранит value, но отображает
labelField. При форматировании чисел и дат важно сохранять
неизменяемость value:
value — идентификаторlabelField — исходный текстrender — отображаемая формаТакое разделение позволяет избегать потери точности при обратном преобразовании данных.
При использовании remote data источников форматирование выполняется после получения ответа:
new TomSelect("#select", {
load: function(query, callback) {
fetch(`/api/orders?q=${query}`)
.then(res => res.json())
.then(data => {
callback(data.map(item => ({
...item,
amountFormatted: new Intl.NumberFormat('ru-RU').format(item.amount),
dateFormatted: new Date(item.date).toLocaleDateString('ru-RU')
})));
});
}
});
Это позволяет разгрузить клиентский рендеринг и стандартизировать формат уже на уровне загрузчика.
При проектировании интерфейсов на Tom Select с числовыми и временными данными формируется три уровня обработки:
render.option и render.itemРазделение этих уровней обеспечивает предсказуемость поведения интерфейса, стабильность отображения и масштабируемость при росте объёма данных.