Вопрос или проблема
Я хочу использовать Choices JS в качестве интерфейса для фильтрации поиска на стороне сервера. Демонстрация включает только загрузку всего списка и фильтрацию на стороне клиента – у меня слишком много записей, чтобы это было жизнеспособно.
Я заставил это работать, за исключением одной вещи: когда вы печатаете, поле поиска теряет фокус, что делает его почти unusable с точки зрения пользовательского опыта.
У меня пока что это (обратите внимание, что это завернуто в связывание Knockout и использует таймаут как фиктивный запрос):
(function()
{
var _getConfig = function(el, f_valueaccessor, allbindings, viewmodel, bindingcontext)
{
var cfg = ko.unwrap(f_valueaccessor());
var res = {
obs: cfg.boundField,
src: cfg.src,
multiple: ko.unwrap(cfg.multiple) || false,
options: cfg.options || {},
_current: cfg.items || ko.observableArray()
};
return res;
};
ko.bindingHandlers.choices = {
init:
function(el, f_valueaccessor, allbindings, viewmodel, bindingcontext)
{
var cfg = _getConfig(el, f_valueaccessor, allbindings, viewmodel, bindingcontext);
cfg.multiple && el.setAttribute('multiple');
var chc = new Choices(el, {
searchChoices: false,
shouldSort: false,
shouldSortItems: false
});
chc.setChoices(function()
{
return cfg.src(chc, { initial: true, term: null });
});
el.addEventListener('search', function(e)
{
console.log('поиск', e);
chc.setChoices(function() { return cfg.src(chc, { initial: false, term: e.detail.value }) });
});
},
update:
function(el, f_valueaccessor, allbindings, viewmodel, bindingcontext)
{
}
};
})();
var model = new function()
{
this.Choice = ko.observableArray();
this.IsMultiple = ko.observable(false);
this.find = function(sender, eargs)
{
return new Promise(function(resolve)
{
setTimeout(function()
{
resolve([{ value: 1, label: 'Привет' }, { value: 2, label: 'Мир' }]);
}, 100);
});
};
};
ko.applyBindings(model);
Работающая петля: https://jsfiddle.net/whelkaholism/5sn9wt0r/22/
ОБНОВЛЕНИЕ:
Я изменил обработчик события поиска на приведенный ниже и он работает, но кажется довольно хакерским, есть ли лучший способ?
el.addEventListener('search', function(e)
{
console.log('поиск', e, chc);
chc.setChoices(function() {
return new Promise(function(resolve){
cfg.src(chc, { initial: false, term: e.detail.value }).then(function(data) {
resolve(data);
setTimeout(function() {chc.choiceList.element.parentElement.querySelector('input[type=search]').focus(); }, 0);
});
});
});
});
Ответ или решение
Вопрос о Choices.js: Фильтрация поиска на серверной стороне
Использование библиотеки Choices.js в качестве пользовательского интерфейса для фильтрации поиска на серверной стороне – это довольно распространённая задача, особенно когда количество записей слишком велико для загрузки и фильтрации на клиенте. Если вы столкнулись с проблемой, когда поле поиска теряет фокус во время ввода, это влияет на пользовательский опыт (UX) и может вызвать недовольство пользователей.
Анализ Вашего Решения
Учитывая ваше текущее решение, вы используете стандартные функции библиотеки Choices.js в сочетании с Knockout.js. Ваша реализация позволяет загружать данные асинхронно по мере ввода текста в поисковое поле, но, как вы отметили, фокус на поле теряется, что делает процесс неинтуитивным.
Текущий Код
Ваш текущий обработчик события поиска:
el.addEventListener('search', function(e) {
console.log('search', e, chc);
chc.setChoices(function() {
return new Promise(function(resolve){
cfg.src(chc, { initial: false, term: e.detail.value }).then(function(data) {
resolve(data);
setTimeout(function() {
chc.choiceList.element.parentElement.querySelector('input[type=search]').focus();
}, 0);
});
});
});
});
Хотя это решение работает, оно действительно выглядит "хакерским". Давайте рассмотрим более элегантный способ:
Оптимизированный Подход
-
Сохранение Фокуса: Вместо использования
setTimeout
, что может быть ненадёжным, сохраните фокус при каждом обновлении списка выбора. Мы также можем использовать методrender
для вызова обновления компонента. -
Использование Асинхронных Функций: Вместо промисов и вложенных функций, используйте
async/await
, что значительно улучшит читаемость кода.
Вот пример улучшенного обработчика:
el.addEventListener('search', async function(e) {
console.log('search', e, chc);
chc.setChoices(async () => {
const data = await cfg.src(chc, { initial: false, term: e.detail.value });
return data;
});
// Сохраняем фокус на поле поиска после обновления
e.target.focus();
});
Итоги
Таким образом, для улучшения пользовательского опыта с Choices.js в контексте серверной фильтрации поиска, вам следует:
- Использовать
async/await
для повышения читабельности кода. - Сохранять фокус на поле поиска с помощью
e.target.focus()
вместо хаков сsetTimeout
.
Такой подход не только улучшит UX, но и сделает ваш код более чистым и простым в обслуживании.
Дополнительные Рекомендации
- Оптимизация Запросов на Сервер: Убедитесь, что ваш сервер обрабатывает запросы как можно быстрее. Использование кэширования может значительно сократить время отклика.
- Пользовательские Сообщения: Если результат поиска не найден, обязательно сообщайте пользователю об этом, чтобы избежать путаницы.
Следуя этим рекомендациям, вы сможете создать более стабильный и интуитивно понятный интерфейс поиска с использованием Choices.js.