Хочу сказать спасибо @tadatuta за ревью кода моего компонента меню представленного на конкурс. Замечания были очень полезные.
Однако часть волнующих меня вопросов не была освещена, потому буду выносить их в отдельные посты на форум. Начну с вопроса обозначенного в заголовке.
Мой компонент построен на блоках menu
, menu-item
, button
и прочих из библиотеки bem-components
.
Для реализации второго уровня в меню, я воспользовался элементом group
блока menu
задав ему абсолютное позиционирование и overflow-y: auto
.
Однако, код метода _scrollToItem
не был рассчитан на такое рассположение пунктов меню и мне понадобилось переопределить его: я скопировал код метода из библиотеки, сделал автоматическое определение контейнера со скроллом и изменил функцию анимации.
Так же мне не понравилось, что вызов метода _scrollToItem
происходит при наведении мышью. Пришлось переопределять метод _onItemHover
.
Потом выяснилось, что клавиатурная навигация для переключения выбранного пункта меню устанавливает ему модификатор hovered
(этому я посвящу отдельный пост). Методы _onKeyDown
и _onItemClick
тоже пришлось переопределять.
Всю эту красоту я оформил, как модификатор menu_fix_scroll
, чтобы не ломать блок menu
в других местах и разместил его как модификатор моего блока kg-menu_fix_scroll
. Этот модификатор указан как зависимость самого блока kg-menu
.
Вот тут я подошёл к вопросам:
- правильно ли я решил, что доопределения библиотечного блока, которые необходимы только моему блоку и нигде более правильно разместить внутри моего блока?
- критично ли, что
kg-menu_fix_scroll.js
фактически описываетmenu_fix_scroll
? - есть ли другие более изящные пути решения такой проблемы?
Сейчас я делаю поле типа autocomplete
в рамках компонента поиска. Есть мысль сделать его на базе dropdown, определив новый модификатор dropdown_switcher_input
.
Снова возникают вопросы означенные выше.
Вариант 1.
В блок dropdown
добавить модификатор _switcher_input
. (Будет странно смотреться, как не совсем самостоятельная сущность. Скорее всего не получится выделить общие правила для отображения попапа, оставив только метод аналогичный onSwitcherClick
)
Использовать его в блоке autocomplete
(или даже в модификаторе _type_autocomplete
блока input
).
Затем всё это использовать это в моём блоке поиска.
Вариант 2.
Определять модификатор dropdown_switcher_input
внутри autocomplete
/ input_type_autocomplete
.
Вариант 3.
Не использовать код dropdown
и реализовать autocomplete
/ input_type_autocomplete
самостоятельно.
Вариант 4.
Только что придумал вариант, похожий на правильный ответ.
Наследовать autocomplete
(input_type_autocomplete
врядли получится?) от dropdown
.
@Guria есть у нас такой компонент) Могу поделиться реализацией! Скажи куда? gist подойдет?
@Guria Вопросы как всегда хорошие.
Начну с главного: использовать существующие блоки для создания собственных — это хорошо и правильно, но при этом важно понимать, что подчеркивание в названиях методов говорит о том, что эти методы приватные и их реализация (и вообще существование) может драматически меняться в том числе в минорных версиях. И такие изменения даже могут быть не отражены в changelog-е. Поэтому единственный по-настоящему надежный способ не прострелить себе ногу — это скопировать такой блок под новым именем к себе на проект. А лучше — избегать завязки на приватные методы. Ну или, если время позволяется, можно попробовать описать причину, почему текущего API недостаточно в issue и убедить мейнтейнеров, что это частный нужный кейс. Есть шанс, что какие-то методы могут быть открыты.
Теперь к конкретике. Главный кейс использования
menu
— это блоки типаselect
,suggest
и т.п. Если хочется сделать что-то принципиально другое, то вполне возможно некоторый копипаст под новым именем окажется надежнее и проще в поддержке — при изменении одного из вариантов использования не придется думать, как это повлияет на все остальные.Если все-таки не копипастить реализацию
menu
под новым именем, то вместо модификатора можно было бы отнаследоваться от него черезbaseBlock
:но нужно понимать, что это по прежнему не дает никакой защиты от возможных изменений в приватных методах
menu
.Хорошим примером реализации блока
autocomplete
может служить библиотека @narqo https://github.com/narqo/bem-suggest/ (она на стадии work in progress, но архитектурно все уже должно быть понятно). Кстати, Вова, у тебя нет настроения добить либу? Она бы многим пригодилась ;)@tadatuta а protected методы вы так не обозначаете? У нас в команде принято, что все, что не публичное - все с подчеркивания начинается, а protected от private отличаем с помощью jsdoc.
@apsavin да, у нас тоже все с подчеркиванием и с использованием
@private
и@protected
. Примеры можно посмотреть вbem-components
иbem-core
@sipayRT угу, мне тоже казалось, что я видел и у вас такоие доки, просто после обсуждения выше открыл https://github.com/bem/bem-components/blob/v2/common.blocks/menu/menu.js и не увидел, что можно переопределять, а что нет. Если док отсутствует - считать приватным?)
@apsavin да, если в имени метода в начале присутствует подчеркивание, то метод по умолчанию является приватным
@sipayRT спасибо А подобные PR вы принимаете?
@apsavin конечно. почему нет? ;)
Offtopic, но открывать отдельный issue на тему мне лениво, sorry
@tadatuta В ближайшее время — нет :( Как показала практика, сделать (универсальный) suggest, который при этом, пригодился бы мне в проекте, за пару часов свободного времени у меня не получилось. Но если доделаю что-то более или менее отчуждаемое (и за что не будет очень стыдно) — дам знать ;)
@apsavin у нас есть задача bem/bem-core#586.
Но, как со всем в этом мире, окончательного мнения по этому поводу нет.
Пример: методы
i-bem__dom:BEMDOM#bindTo
,i-bem__dom:BEMDOM#emit
(как и многие другие, необозначенные), по определению являются protected. Вызывать их у инстанса, с точки зрения архитектуры — «звоночек про code smells». Но и писать в каждом блокеthis._emit('click')
многим не хочется.В то же время надо отличать protected-метод, который я (как разработчик блока) переопределяю в модификаторе, от protected-метода, который пользователь может вызывать на своем уровне переопределения (или в блоке наследнике).
Вопросов много, универсального решения, пока, не видно. Но и не сказать, что это «насколько сильно всем мешает, что руки чешутся закрыть поскорее задачу» ;)
Offtopic2: Такие же проблемы есть и у Java/C++/C#-ребят, где вроде чесное ООП и все такое. Потому там давно уже изобретают
@friendly
,@inner
и пр.Мы именно такого принципа и стараемся придерживаться: «можно пользоваться всем, на что есть документация». Всегда писать, что метод (или поле), имя которого начинается с подчеркивания — приватный, кажется избыточным.
@narqo Спасибо за такой подробный комментарий.
Хорошая задача.
Понимаю тему про
emit
. Я был бы рад, если бы такие методы начинались с подчеркивания, мне не сложно его писать - но я не перфекционист, в данном случае обратная совместимость, как мне кажется, важнее.Всегда писать jsdoc - это хорошо, на самом деле. Это дает определенность, уменьшает количество правил, даже IDE, в конце концов, начинает понимать код лучше.
Кажется, что это действительно обычная практика для многих проектов. я, правда, в последнее время вижу все меньше наследований и грань между private/protected сильно затирается для не владеющих тяжелым ооп.
Еще я с этим сильно сталкивался при валидации jsdoc для этих самых методов с
_
— нашел, что часть комьюнити использует_
в начале, часть — в конце (всякий closure compiler, angular), но различий между private/protected, кроме как через@access
или@protected
, я не нашел. Редкие проекты используют двойное подчеркивание, некоторые — еще какие-то варианты, но это скорее исключения.