Vue. Компонент для мультивыбора (MultiSelect) - insbor.ru
insbor.ru

insbor.ru

Привет, это я

Читаю, пишу, перечитываю и исправляю.


Что здесь происходит


Предыдущие записи


Vue. Компонент для мультивыбора (MultiSelect)

Опубликовано :   |  Кем :   |  Категория :  Vue

Продолжаем знакомиться с компонентами Vue.js. Сегодня реализуем выпадающий списокс авто-дополнением для выбора нескольких опций. Задача довольно популярная - так можно выбирать страны, регионы, пользователей, тэги - что угодно. Я использую знакомый по предыдущей записи классификатор категорий товаров с Яндекс.Маркета.

За объект для творческого плагиата дизайна возьму плагин jQuery Chosen. Выбранные опции будем выводить в немаркированном списке, заставив его с помощью CSS (а именно, flexbox) расположить опции в линию. За выбранными опциями будет маленькое прозрачное текстовое поле с невидимыми границами, которое нужно только для вызова выпадающего списка и фильтрации списка доступных для выбора опций. Блок с выпадающим список ничем хитрым не отличается, можете посмотреть в исходном коде. Нужно только правильно позиционировать его на странице, ограничить размеры и задать прокрутку содержимого по вертикали.

Поскольку я хочу в зависимости от набранного текста подгружать доступные для выбора опции с удалённого сервера, то уже выбранные пользователем надо копировать в отдельный массив selectedOptions, а элементы в выпадающем списке - в droppedDownOptions. Вот, что получится в шаблоне:

Какие проблемы теперь нам надо решить:

  • При выборе слишком большого числа опций последующие нужно переносить на новую строку, растягивая компонент по вертикали
  • При изменении ширины компоненты выбранные опции должны распределяться по рядам, чтобы вписаться в новую ширину, растягивая компонент по высоте
  • При удалении выбранных опций, оставшиеся должны сдвигаться влево, занимая свободное место 
  • Если мы выбрали, скажем, "Авто", а затем начали набирать в текстовом поле "Быт...", то список доступных для выбора опций изменится, но на ранее выбранные опции это не должно повлиять

К сожалению, у меня не хватило навыков вёрстки для правильной реализации изменения размера компонента с помощью CSS, так что пришлось наскоро написать костыль на JS:

Обратите внимание, что команды на перерисовку компонента неправильно вызывать непосредственно в его методах. Для этого нужно использовать Vue.nextTick(callback). Тогда callback будет вызван после следующего цикла перерисовки DOM.

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

Реализация Http.ajaxAction(url, data, callback) очевидна. App.eventOnElement(event, container) проверяет, произошло ли событие event в координатах экрана элемента container. Можно использовать jQuery, но для таких мелочей я предпочитаю освежить в памяти чистый javascript.

Вся остальная логика компонента ещё проще, не вижу смысла приводить её фрагментами. Для тренировки лучше реализовать самому, а для ознакомления - посмотреть модуль целиком в репозитории.

Осталось с помощью CSS добавить оформление и дело в шляпе! Получится что-то вроде этого:

Пример выпадающего списка

Исходный код компонента и серверной части на основе списка категорий товаров Яндекс.Маркет доступен на github. Ну и демо, конечно.