В типичных сценариях автодополнения отображаемое значение и
фактическое значение, которое требуется отправить на сервер, не
совпадают. Awesomplete работает с массивами строк, но реальные формы
часто требуют более сложной модели данных: идентификатор записи, код
справочника, UUID или числовой ключ вместо человекочитаемого названия.
Именно здесь используются скрытые поля (hidden input),
позволяющие разделить UI-представление и данные, уходящие в форму.
Базовая проблема возникает при использовании списков вида:
Москва77Awesomplete по умолчанию возвращает строку, которую выбрал пользователь. Однако форма должна отправить не текст, а идентификатор.
Решение заключается в использовании пары полей:
Простейшая HTML-структура:
<input id="city" type="text" />
<input id="city_id" type="hidden" />
При инициализации Awesomplete можно использовать массив объектов, где хранится и метка, и значение:
const cities = [
{ label: "Москва", value: "77" },
{ label: "Санкт-Петербург", value: "78" },
{ label: "Казань", value: "16" }
];
Инициализация:
const input = document.getElementById("city");
const hidden = document.getElementById("city_id");
const awesomplete = new Awesomplete(input, {
list: cities.map(c => c.label)
});
На этом этапе Awesomplete работает только с отображаемыми строками. Для привязки значения требуется обработка выбора.
Awesomplete предоставляет событие
awesomplete-selectcomplete, которое вызывается после выбора
элемента из списка.
input.addEventListener("awesomplete-selectcomplete", function(event) {
const selectedLabel = event.text.value;
const selectedItem = cities.find(c => c.label === selectedLabel);
if (selectedItem) {
hidden.value = selectedItem.value;
}
});
Здесь происходит ключевой процесс:
Использование поиска по label через find
работает только при уникальных значениях. В реальных справочниках
возможны совпадения:
Поэтому более надёжный подход — связывать данные через индекс или расширенную структуру Awesomplete.
Awesomplete поддерживает работу с объектами, если задать
label и value, но рендеринг нужно
контролировать через item и replace.
const awesomplete = new Awesomplete(input, {
list: cities,
item: function(text, input) {
return Awesomplete.ITEM(text.label, input);
},
replace: function(text) {
this.input.value = text.label;
}
});
Теперь объект полностью доступен внутри логики выбора, и скрытое поле можно обновлять без поиска по массиву:
input.addEventListener("awesomplete-selectcomplete", function(event) {
const item = event.text;
hidden.value = item.value;
});
В реальных формах важно учитывать не только выбор из списка, но и ручной ввод. Пользователь может:
Поэтому hidden input должен очищаться при любом изменении текста:
input.addEventListener("input", function() {
hidden.value = "";
});
Это предотвращает ситуацию, когда отправляется устаревший id при изменённом тексте.
Альтернативный подход — хранить идентификатор прямо в DOM через
data-*:
input.dataset.selectedId = "";
Обновление при выборе:
input.addEventListener("awesomplete-selectcomplete", function(event) {
input.dataset.selectedId = event.text.value;
});
Однако такой подход менее надёжен при отправке формы, поскольку
dataset не участвует в form submission напрямую.
При отправке формы hidden input становится основным источником данных:
form.addEventListener("submit", function(e) {
if (!hidden.value) {
e.preventDefault();
alert("Необходимо выбрать значение из списка");
}
});
Это позволяет обеспечить строгую валидацию: текст в input не считается валидным без соответствующего id.
Во многих приложениях список загружается с сервера:
fetch("/api/cities")
.then(res => res.json())
.then(data => {
awesomplete.list = data.map(c => ({
label: c.name,
value: c.id
}));
});
Важно учитывать, что данные могут обновляться динамически, и связь label/value должна оставаться стабильной.
В расширенных сценариях одно автодополнение может управлять несколькими скрытыми значениями:
HTML:
<input id="location" type="text" />
<input id="location_id" type="hidden" />
<input id="location_type" type="hidden" />
Логика:
input.addEventListener("awesomplete-selectcomplete", function(event) {
hiddenId.value = event.text.value.id;
hiddenType.value = event.text.value.type;
});
В этом случае value становится объектом, а не
строкой.
При реализации форм с возможностью редактирования требуется корректный сброс:
function resetField() {
input.value = "";
hidden.value = "";
}
Особенно важно при:
Скрытое поле должно соответствовать текущему тексту. Проверка:
function isValid() {
return input.value.length > 0 && hidden.value.length > 0;
}
Более строгий вариант:
function isConsistent() {
const match = cities.find(c => c.label === input.value);
return match && match.value === hidden.value;
}
При использовании filter, sort или
кастомных источников важно сохранять структуру объекта:
const awesomplete = new Awesomplete(input, {
list: cities,
filter: function(text, input) {
return text.label.toLowerCase().includes(input.toLowerCase());
}
});
Hidden-поле при этом остаётся независимым от механизма фильтрации и обновляется только при выборе.
Удаление содержимого input должно автоматически сбрасывать hidden:
input.addEventListener("keyup", function(e) {
if (e.key === "Backspace" && input.value === "") {
hidden.value = "";
}
});
Это защищает от ситуации, когда пользователь очищает поле вручную, но скрытый id остаётся.
При автозаполнении формы важно синхронизировать оба поля:
function setCity(label, id) {
input.value = label;
hidden.value = id;
}
Без этого Awesomplete не участвует в процессе, но состояние формы должно оставаться целостным.
В сложных интерфейсах hidden поле становится частью состояния:
Рекомендуется централизовать обновление:
function updateState(item) {
input.value = item.label;
hidden.value = item.value;
}
И использовать её во всех сценариях: выбор, загрузка, сброс.
find по label без уникальностиПри нескольких автодополнениях на странице важно избегать перекрёстного влияния:
function bindAutocomplete(inputId, hiddenId, list) {
const input = document.getElementById(inputId);
const hidden = document.getElementById(hiddenId);
new Awesomplete(input, { list });
input.addEventListener("awesomplete-selectcomplete", function(e) {
hidden.value = e.text.value;
});
input.addEventListener("input", function() {
hidden.value = "";
});
}
Это обеспечивает изоляцию состояния между полями.