Программист должен упрощать жизнь пользователю, а не себе.
(конечно, есть нюансы)
Автор статьи
Был ComboBox
Статья описывает идею визуального веб-контрола для выбора элемента из списка. Эта идея о том, как можно эволюционировать ComboBox
(он же DropDown
, он же select
в html) для повышения удобства программного продукта для пользователя – чтобы контрол выбора стал намного удобнее и дружелюбнее при выборе сложных объектов из больших и не очень списков. Ведь задача программиста — в непрерывном улучшении и упрощении жизни пользователя.
Это идея, реализация которой есть только частичная и только для ASP.NET Web Forms в связке с devexpress – поскольку у меня большой бекграунд именно на этой связке технологий.
Эта статья не для тех, кто хочет скопипастить код, нажать F5 и увидеть результат. Ее корректнее всего было бы отнести к документам, которые называют Функциональными Требованиями или даже Функциональным Дизайном. Поэтому, если вам интересен взгляд на эволюцию удобства, то, надеюсь, статья будет полезна.
Эволюция ComboBox’а – что уже есть
Сначала приведу список удобных фич, которые уже есть в хороших реализациях визуальных контролов. В этом параграфе нет ничего нового, только описание функциональности, уже реализованной в передовых библиотеках визуальных контролов.
Например, у devexpress фичи относительно select
в html такие (в порядке, на мой взгляд, уменьшения необходимости, то есть сначала самое важное):
- Фильтр по введенной строке:
По мере введения символов в контрол список фильтруется. Тут замечания:
— По каким полям проверять вхождение введенной строки? Если проверять по тем, которые в контроле не отображаются, то пользователю может быть неочевидно поведение. Но иногда такое может требоваться. А если пользователь вводит строку, содержащую в себе границу при конкатенации полей? То есть, если выводится ФИО сотрудника, скажем, «Черняев Константин», а имя и фамилия хранятся в разных полях данных, и пользователь вводит «яев конс» (здесь и далее регистр символов не имеет значения – считаю, что при поиске текста это стандарт де-факто). Дружелюбно со стороны контрола было бы найти эту запись:
— Обычно сортировку делают по алфавиту. И когда пользователь вводит строку (скажем, «стол»), и такой элемент в списке есть, причем есть и такие элементы, которые эту строчку содержат, но не равны ей («столб», «апостол», «пистолет»), то получится, что пользователю приходится листать список, выбирая нужный (а порядок в этом примере будет такой: «апостол», «пистолет», «стол», «столб»). Причем уточнить запрос и сузить список невозможно. Если список большой, это большой минус к дружественности.
Решением этой проблемы будет сортировка по признаку «равенство/вхождение». То есть первыми должны идти записи, равные введенной строке, а потом остальные. Я бы даже пошел дальше – сначала равные, потом начинающиеся с этой строки, потом остальные. - Автодополнение, автовыбор первого из найденных:
По мере набора текста контрол фильтрует данные, показывает их, и дополняет вводимый текст
первым из найденных. При потере фокуса будет выбран он же.
Замечание – если пользователь вводит символы не с начала нужной ему строки, автовыбрать ее ему не удастся. - Можно включить режим, когда можно написать новое значение, которого нет в списке. Иначе – нельзя ввести текст, по которому не найдено ни одного элемента. Как бы режим подсказок, когда ввести можно не только то, что подсказывают.
Такой режим упрощает жизнь пользователя, когда у него должна быть возможность добавления нового значения, которое не является сложным объектом – просто строчка. Например, имя. Или должность на предыдущем месте работы – она может быть любая, но есть список уже известных должностей. - Источником данных, то есть самим списком может быть как javascript-массив на клиенте (то есть уже скачанные данные), так и ajax-запрос на сервер (то есть когда клиент скачивает данные ровно по запросу).
- Virtual scrolling – скролл списка без скачивания всех данных. Продолжение предыдущего пункта. Если данных очень много, то скачиваются только видимые элементы или с небольшим запасом, а при прокрутке списка подкачиваются нужные.
Если элементов в списке много, то не стоит их все хранить на клиенте – пусть подтягиваются по мере необходимости. А если мало, то можно заранее, при загрузке страницы, их скачать, и хранить в памяти браузера. Сколько мало, а сколько много — сказать загодя нельзя, нужен оптимизационный баланс при сравнении «скачать сразу все данные» vs «при прокрутке видимого окна данных скачивать следующую страницу». - Есть возможность запрограммировать кнопки в контроле, например, «сбросить значение», «следующий/предыдущий элемент». Или любое другое нужное пользователю действие. Пример добавления кастомных кнопок:
В комбике на скриншоте убрана обычная кнопка-треугольник
, но добавлено три пользовательских.
- Табличный вид списка:
Можно структурировать показываемые данные и отображать не только название элемента, но несколько его полей. - Стили для ячеек при табличном виде, иконки, шаблоны элемента:
- Можно еще упомянуть возможность выбора в виде
ComboBox
’а элемента из иерархического списка, но это другой контрол (DropDownEdit, хотя заголовок у него Tree List Lookup).
Эволюция ComboBox’а — новое
Пользуясь перечисленными возможностями, я постепенно приходил к тому, что не хватает дальнейшей эволюции ComboBox
’а:
- Легкое добавление кнопки «Сбросить значение». Без программирования.
Такая кнопка нужна весьма часто, поэтому удобно, когда она добавляется присвоением простомуboolean
-свойству контрола:<comboBox showResetButton=”true” />
- Показать, что выбранное значение было изменено.
Практически любой текстовый редактор показывает, что файл был изменен и не сохранен. Такая возможность кажется удобной и на контролах редактирования. Это касается, конечно, не толькоComboBox
’а, а вообще всех эдиторов.Возможно, стоит добавить возможность откатить изменение в контроле редактирования. Как бы cntr+z.
- Ссылка на страницу выбранного элемента. Если, конечно, у сущности элементов списка есть своя страница.
Например, пользователь делает заказ и выбирает пункт самовывоза. И естественно захотеть при выборе, да и после выбора, получить полную информацию о выбранном пункте (адрес, как добраться, схема и фото) без лишних телодвижений. Один раз реализовав такую возможность, уже хочется, чтобы в любом комбике она была.
Самым удобным кажется рядом с визуальным контролом написать обычный тег
<a href='entity?id=...'>i</a>
Можно и сам текст выбранного значения сделать ссылкой, но не стоит – ведь при нажатии на комбик обычно открывается список выбора. Хотя так делают – тогда для открытия списка пользователю нужно нажать на кнопку треугольника
.
Пример реализации этих трех пунктов:
Звездочка означает, что значение было изменено (можно ее показывать как в
caption
, так и справа от контрола, это дело стиля конкретного проекта – обычно ведь звездочка означает, что значение обязательно для выбора), красный крестик – кнопка «Сбросить значение», i – ссылка на карточку выбранного филиала, кнопка с троеточием – для пункта 4. - При наличии у сущности страницы-каталога с возможностями различных фильтров, удобной визуализации, пейджинга, сортировки etc – выбор сделать с помощью этой страницы-каталога. Это кардинальное улучшение делает
ComboBox
CatalogBox
’ом –ComboBox
’ом XXI века.Например, выбирает пользователь клиента, чтобы подставить в договор и
ComboBox
ищет по выводимым данным (скажем, номер и название клиента), а пользователь не знает имени, только, например, ИНН, или телефон, или даже просто фамилию директора искомого юрлица. Тогда он открывает страницу-каталог клиентов, ищет нужного с помощью фильтров, копирует номер/код/название клиента, закрывает каталог, вставляет из буфера вComboBox
. Но наша задача — сделать пользователю удобнее (см эпиграф).А для пользователя будет удобно каталог открывать в модальном окошке при нажатии на специальную кнопочку на
CatalogBox
’е (кнопка-троеточие).
Пример страницы-каталога:
На скриншоте каталог открыт в модальном окошке после нажатия на кнопку-троеточие.
Но сама страница каталога не должна быть привязана к модальности – ее должно быть можно открывать как отдельно и независимо, так и в модальном окне. В модальном окне у грида должна появляться возможность выделить одну выбранную строку (или несколько), после чего становится активной кнопка «Выбрать» (на скриншоте онаdisabled
, потому что выбор не сделан). При ее нажатии выбранное значение подставляется вComboBox
, то есть теперьCatalogBox
. - Иногда требуется делать выбор нескольких элементов из списка. Как бы
CheckBoxList
(картинки), но раз уж естьCatalogBox
и можно делать выбор с удобными фильтрами, то хочется множественныйCatalogBox
. Тут естественны функции: добавить, убрать. Иногда требуется ввести валидацию на количество выбранных элементов – расширениеrequired
.Например, нужно в заказ добавлять товары и хочется дать возможность пользователю выбирать их сразу несколько, но
CheckBoxList
неудобен ввиду большого количества товаров. Тогда пользователю будет удобно в каталоге с помощью фильтров выбрать несколько товаров, нажать «Добавить», и потом, при необходимости, добавить еще.
Визуальный пример:Как видно, в левом столбце грида стоит
CheckBox
– таким образом можно выбирать несколько элементов.Эта возможность уже полностью ломает
ComboBox
– он не предназначен для хранения нескольких выбранных значений. Получаем для общего случая творческую изобретательскую задачу, вариативную в реализации.Приведу визуальный пример сделанного выбора.
— Если выбран 1 элемент:— Если выбрано 2 элемента:
— А так можно просмотреть список выбранных элементов и удалить ненужные:
- Иногда необходима возможность выбрать какой-то один элемент из двух списков.
Например, нужно выбрать кто будет доставлять заказ – либо сторонняя компания, либо свой курьер. Можно ввести общую родительскую сущность («исполнитель доставки»), но нет смысла в странице-каталоге для нее. Поэтому оказывается нужна возможность выбора в одинComboBox
из разных списков.Удобнее всего для пользователя этот выбор реализовать на одном контроле
CatalogBox
, но сделать 2 (или более) кнопочки, открывающие разные страницы-каталоги (разных сущностей). И как значение хранить не только выбранный объект, но и его сущность.Пример:
В остальном все так же, как раньше – просто к кнопке-троеточию добавляются еще кнопки выбора.
- Как объединение предыдущих двух пунктов, разнородный множественный выбор.
Например, если нужно выбрать несколько исполнителей доставки из примера предыдущего пункта.
Практически в любом веб-приложении очень нужны первые 4 пункта – кнопка «сбросить значение», выделение изменений, ссылка на страницу выбранного элемента и выбор с полноценными фильтрами. Остальные дополнения нужны далеко не всем проектам. Но для большой информационной системы кажется в любом случае необходимыми все пункты.
Полные Функциональные Требования
CatalogBox
- Кнопка-треугольник (показывающая список в виде обычного выпадающего списка):
a. Возможность задать настройками, показывать или нет кнопку-треугольник.
Для больших списков кнопка «показать весь список» может не иметь большого значения, имеет значение только фильтрованный список. А выбирать критерий, большой это список или нет, должен архитектор интерфейса, а не сам контрол – поэтому нужна настройка.
b. Для разнородного выбора (выбора значения из двух списков) нужно либо делать более одной кнопки-треугольника, либо ни одной, если созданы кнопки-троеточия (открывающие модальную страницу-каталог).
c. Открывать ли список при нажатии на контрол.
d. Открывать ли список при фокусе на контрол. - Кнопка-крестик (сбрасывающая выбранное значение).
a. Связана с валидацией. Если выбор обязателен – то крестика не должно быть.
b. Для множественного выбора – очистить полностью выбор. Стоит также сделать кнопку удаления одного выбранного значения. - Ввод текста в контроле и выбор элемента:
Идеальным кажется такое поведение. Пользователь вводит текст, ищутся элементы, показываются найденные (с учетом авторизационного фильтра, заданного фильтра, фильтра от выбора значений в других контролах, настроек окна virtual scrolling). При потере фокуса выбирается:
— Ничего не найдено: выбор не меняется.
— Найден 1 элемент: выбирается он.
— Найдено больше 1: настройка: выбирается либо первый, либо выбранный до этого.
Выбор элемента мышкой, скролом, стрелками-ентером. Список либо полный, либо подкачивается по мере надобности (virtual scrolling). - Поиск элементов, фильтры:
Фильтр по введенной строке – сортировка: 1) имя равно введенной строке, 2) имя начинается с нее, 3) кончается на нее, 4) внутри нее.Необходимые фильтры:
a. Заданный постоянный фильтр для этого инстанса контрола;
b. Продуцированный другими контролами страницы фильтр;
c. Авторизационный фильтр. Этот фильтр должен накладываться на серверной стороне – это правило безопасности.
Передача всех этих фильтров в страницу-каталог с указанием, что пользователю нельзя их менять. - Отображение найденных элементов:
a. Возможность табличного вида, шаблоны ячейкам, стили/форматы.
b. Выделение найденного текста в элементе. - Отображение выбранного (выбранных при множественном выборе) элемента:
Теоретически возможные варианты:
— шаблон дляjs
-аналогаstring.Format
с указанием path’ов (например, «{Name} ({Id})
»);
— текстоваяjs
-функция;
— Html шаблон.
Если мы остаемся в рамках исходного тега htmlselect
, то возможны только текстовые варианты, а если разметка полностью кастомная, то простор для фантазии есть. - Маркер «значение изменено» с показом старого значения в
title
(всплывающей подсказке). Title
илиbaloon
для показа информации о выбранном элементе, не поместившейся в видимую область бокса.- Ссылка на страницу выбранного элемента.
Учесть, что не у всех сущностей есть страница (но у всех элементов, если есть у сущности). - Кнопка-троеточие.
Открывает страницу-каталог данной сущности в модальном окне. После выбора в каталоге элемента он передается в CatalogBox. - Разнородный выбор.
Более одной кнопки-троеточия, открывающих разные каталоги в модальном окне. Настройка иконок для кнопок,title
. - Множественный выбор.
Отображение более одного выбранного значения, удалить одно или все сразу. - Разнородный множественный выбор.
- Если стиль сайта этого требует, то кнопочки показывать только при наведении мышки на
CatalogBox
. - Валидация. Показывать состояние невалидности только после нажатия кнопки.
Страница-каталог
- Может открываться как независимо, по ссылке, так и модально, по нажатию на кнопку-троеточие.
- Фильтр как внешний параметр, в querystring. Возможности фильтра должны позволять фильтрацию, необходимую для
CatalogBox
’а. - Кнопка «сохранить фильтр в буфере обмена» — чтобы послать ссылку, по которой откроется точно выбранный список.
Также в querystring можно сохранять сортировку и пейджинг. - Параметр, определяющий, можно ли переданный фильтр пользователю изменить (только переданный). Нужен, чтобы
CatalogBox
мог открыть свой каталог, в котором пользователь не смог бы выбрать ничего запрещенного. - Для модального режима параметр — одиночный/множественный выбор.
Для множественного выбора, скорее всего, самый удобный пользователю вариант — чтобы показывалсяcheckbox
на каждый элемент. - Способ отображения списка элементов — обычно
grid
. Но может быть иlist
илиtree
. - Кнопка «обновить» — чтобы можно было обновить список без обновления страницы, то есть без сбрасывания выбранных фильтров.
- Кнопка «сбросить фильтр». Но не сбрасывать переданные во внешнем параметре фильтры из п2.
Для модального режима (то есть при связке с
CatalogBox
’ом): - Без шапки, без футера, меню и прочего, что не касается непосредственно выбора элемента.
- Все ссылки на странице должны открываться в новом окне/вкладке (
target=”_blank”
). - Кнопка «выбрать». Пока не выбран ни один элемент,
disabled
. Когда выбирается первый, становится активна. При нажатии каким-либо образом шлет выбранный элемент (или элементы) породившемуCatalogBox
’у. - Кнопка «Отмена». При нажатии закрывает модальное окно. Так же, как и кнопка-крестик.
- При повторном открытии есть два варианта — перезагрузить страницу или не перезагрузить, так, чтобы остались выбранными фильтры/сортировка/пейджинг.
- Отображение с учетом авторизации – пользователю может быть недоступна какая-то часть списка элементов. Или даже какие-то фильтры. Но это правило для всего сайта, страница-каталог тут ничем не отличается.
PS
Коллеги, прошу вас, если знаете, где реализована похожая функциональность выбора объекта с помощью полнофункциональной страницы-каталога, пишите в коментах. Я нигде — по крайней мере, в веб-мире — не видел подобных реализаций.
Автор: Константин