30 August 2015

Мультиселект и автокомплит на AngularJS

Website developmentJavaScriptAngular
Со списками множественного выбора на Ангуляре всегда было неважно. Существующие решения либо обертки над jQuery-плагином, либо выглядят как не пойми что, либо просто корявы. И у всех естественно особое уникальное АПИ, как будто пользователям делать больше нечего как вникать в ход мыслей разработчиков каждого плагина. Меня такое положение дел не устроило, поэтому написал свой велосипед. Спустя год он дозрел до публикации.

Та-дам! (и забавная история вконце)

tamtakoe.github.io/oi.select
image

Прежде всего решил: никакого своего АПИ, никаких сторонних библиотек и никакого своего дизайна. Селект должен быть максимально приближен к стандартному только с возможностью автодополнения и создания в нем новых опций. АПИ так же должен быть совместим с Angular select, который в свою очередь совместим с HTML select. Стандартный внешний вид использовать не получилось, т. к. он определяется браузером, поэтому взял за основу наиболее распространенный Bootstrap. По-сути, получился не новый компонент, а расширение существующего.

Необходимо ценить время своих коллег, не вынуждать их изучать новый формат параметров, чтобы сделать то, что они давно умеют делать; считаться с навыками пользователей, которые ожидают от компонента привычного вида и поведения. Первое, что должен сделать разработчик при проектировании элементов интерфейса — убить в себе дизайнера. Не в том плане, чтобы сделать дизайн кое-как, а чтобы максимально использовать существующее поведение, добавлять от себя в крайнем случае, а менять в самом крайнем.


АПИ получилось сделать полностью совместимым, за исключением задания опций через <option>. В HTML такой способ используется из-за того, что другого нет. В Ангуляре же есть контроллеры и модели, там этот способ был оставлен для совместимости. Мне было лень делать такую совместимость. Может быть когда нибудь…

В самом начале возникло три пути создания подобного компонента: реализация на чистом JS с манипуляциями DOM и проч., расширение ангуляровской директивы select, реализация преимущественно на Ангуляре. Т.к. я не настолько умен, чтобы принимать такие решения в голове, то просто взял и реализовал все три способа:

  1. Самое заманчивое — написать свой select по аналогии с ангуляровским. Чистый JS, максимальная производительность, всё под контролем. Но сделать такое оказалось не просто: слишком много нюансов, требуется знание Ангуляра на самом низком уровне, много копипаста функций, которые Ангуляр реализует внутри. В итоге все это вываливается в многие тысячи строчек кода со всеми вытекающими. Отказался от этой затеи, хотя для простых компонентов она бы подошла.
  2. Можно использовать в своей директиве Directive Definition Object ангуляровского select. Расширить его, переопределить методы и т. п. Звучит хорошо, но на деле получается слишком костыльно. Все-таки Ангуляр пока не дает возможность для расширения своих компонентов, особенно директив (к сожалению), поэтому расширяя средствами JS вы завязываетесь на внутреннюю реализацию и рискуете потерей обратной совместимости. Такой способ допустим, в директивах, где расширение предусмотрено разработчиками, например Angular-bootstrap popover.
  3. Проще и нагляднее оказалась реализация средствами Ангуляра. Из копипаста только Regexp для парсинга параметров из ng-options. Код проще чем на чистом JS и не требует знания внутреннего устройства ангуляровского селекта. Производительность хорошая (за что я больше всего боялся). Думаю, этот способ подойдет для реализации большинства компонентов.


Не смотря на совместимость по АПИ, уникальных параметров хватает. Полагаю, там еще не всё гладко и очевидно, так что буду рад замечаниям в комментариях. Много чего относится к созданию новых элементов из строки ввода. После некоторого анализа пришел к выводу, что существует два случая использования поля ввода с подсказками:

  • prompt — поле работает как обычный инпут, а в списке просто выводятся подсказки. По нажатию Enter в модель попадает значение из поля. Поле можно очистить и тогда в модели будет пустая строка. Такое поведение характерно для поисковой формы.
  • autocomplete — поле работает как список с вариантами. По нажатию Enter в модель попадает первый вариант из списка и только если там ничего не было — содержимое поле ввода. Записать в модель пустую строку нельзя. Такое поведение характерно для формы ввода тегов.

Очередной спорный пункт: кастомное оформление опций. В стандартном компоненте (даже ангуляровском, даже бутстраповском) ничего подобного нет. Но всем хочется. Пришел к компромиссному решению — сделать поддержку фильтров для всего и вся. Конечно, придется писать HTML в JS, зато и быстродействие выше и значительно проще чем заморачиваться с поддержкой шаблонов (да и какие могут быть шаблоны для элемента списка?). Впрочем, когда-нибудь…

А теперь обещанная забавная история для осиливших страницу текста без картинок.

В одной из версий была бага, которую никто из разработчиков веб-отдела не мог воспроизвести. Зато остальные сотрудники: менеджеры, аналитики, тестировщики, 1С-программисты воспроизводили в любом случае. Доходило до слез — за одним компьютером одни люди всегда выбирают элемент из списка, у других это ни получается как бы они ни старались. Думаю, это свойство можно использовать при приеме людей на работу. Плохие программисты тест не пройдут. Проверь себя и ты, читатель.
Tags:angularjsjavascriptmultiselectautocompleteselectpluginмультиселектавтокомплит
Hubs: Website development JavaScript Angular
+18
23k 163
Comments 71
Popular right now
Javascript разработчик
from 2,800 to 3,300 $ArtezioМинск
Frontend разработчик
from 175,000 ₽FASTVPSСанкт-ПетербургRemote job
Team lead JavaScript developer
from 180,000 ₽HolyWebRemote job
Frontend (Angular) developer
from 140,000 to 180,000 ₽МТСМоскваRemote job
JavaScript разработчик
from 150,000 to 200,000 ₽SportrecsМоскваRemote job
Top of the last 24 hours