Так получилось, что блок изначально есть на странице, но внутри элемента с display: none
. И если блок в этот момент проинициализируется, то он неправильно всё посчитает.
Я решил сделать так:
{
beforeSetMod: {
js: {
inited: function() {
if (this.domElem.is(':hidden')) {
return false;
}
}
}
},
}
а когда внешний элемент меняет свою видимость, то делаю
BEMDOM.init($elem);
Однако ничего не работает ;) Вложенный блок считает, что его уже пытались проинициализировать и не хочет инициализироваться повторно.
Возможно я что-то делаю совсем не так и проблему нужно решать по другому?
Есть вот такое решение. Ориентировано на backbone, но портируется на i-bem очень легко. Если упростить, то: Суть в том, что все блоки, которые что-то показывают, триггерят событие в канал. Те блоки, которые зависят от того, видимы они или нет, регистрируют себя в некотором менеджере, который при изменении видимости вызывает публичный метод update у тех из них, которые стали видимы.
Может показаться, что решение заоверинженерено, но на самом деле централизация такого рода спасает от багов.
Если хотите - могу выложить порт на гитхаб.
@h4 Вложенный блок не получится переписать на live-инициализацию и инитить из родителя, например, через
findBlockInside()
, когда будет нужно?@apsavin да, идея понятна. Но получается, что у меня элемент блока Outer должен знать что внутри него есть блок Inner, который нужно передёрнуть, если видимость элемента сменилась.
@tadatuta это блок-график. Нужно чтобы он был воткнут куда угодно и работал. Те есть более частый случай — нормальная инициализация.
@h4 предложу:
init()
.init()
и все работает as is.init()
запускался поллинг про видимость (можно заиспользовать tick) и, как только блок станет видимый, дернетinit()
.@tadatuta ну вот куда не посмотри — везде костыли. Хорошо было бы иметь возможность принудительного инита, что ли.
Так обращение к блоку через
find*
-методы и работает как принудительный инит + есть live-init по разным событиям. А как ты себе представляешь решение?findBlockInside()
хочет принимать в себя имя блока. А я не хочу их туда передавать.Я хочу, чтобы
BEMDOM.init($ctx, true)
пробовал ещё раз проинициализировать все блоки внутри.В общем пока что сделал так:
beforeSetMod: function() {...}
.Тоже не фонтан решение, но так хоть никто явно ни с кем не связан.
Я завел issue на подумать.
@h4 нет, не должен внешний блок знать ничего про внутренний, в том и идея. Еще раз: блоки, которые меняют видимость чего-то - просто кидают событие в канал, что они что-то показали. Блоки, которым нужно обновиться, когда они станут видимы - просто регистрируются у менеджера. Менеджер слушает события в канале и дергает update у тех блоков, у которых нужно. В результате какой-нибудь попап ни сном ни духом про то, что внутри него график. Он просто сообщает, что вот он я, показал кусочек DOM'a. И график ни сном ни духом, что он в попапе.
Видимо, опасение было в том, что manager все равно знает про всех, а хочется без этого. Но если есть канал и абстрактные подписки, то кажется, что менеджер знает только про то, что он должен кого-то дергать, кто сам возжелал через канал. Возжелание = какое-то событие в канале, видимо. Да?
У менеджера есть просто набор инстансов блоков, про которые он знает только, что у них есть, скажем, два метода - isVisible и update. То есть что они реализуют интерфейс какой-то (IVisibilityListener, если угодно). Регистрацию блоков можно по-разному реализовать. Можно через событие, как ты предложил. Можно более явно (мне так больше нравится), через ym-зависимость от менеджера, который сделать тогда синглтоном.
У нас такой подход про visibility блоков используется в продакшене пару лет уже как минимум. Это не аргумент за, но что-то да значит.
Да, простите. Менеджер в любом случае работает с интерфейсом, которыё реализуется блоками. Hard day's night ;-(
:+1:
А, вот еще мысль: С SPA синглтон вполне подходит, но т.к. он синглтон, то его тестировать не фонтан, и можно намиксовать на нужные дом-ноды элемент менеджера или блок-хелпер (если не хочется иметь менеджера в body), и там указать нужный блок, который надо регистрировать (зная, что по определению нельзя 2 инстанса одного и того же блока на одной дом-ноде иметь). Решение немного функционально отличается, более гибкое, но более замороченное, конечно ;-). Оверинжиниргом попахивает.
Хотя, если это сделать один раз и покрыть тестами (кода, вроде, не много), то должно быть более универсальным, чем синглтон. Мало ли какие-то из инстансов блоков не нужно будет через менеджер прогонять.
Да, можно в эту сторону подумать и, наверное, кое-какие моменты улучшить в реализации. i-bem позволяет, Backbone нет.) Но я больше подходом делился. Как реализовать - тут @h4 сам, наверное, решит, если вообще захочет реализовывать.
@apsavin @zxqfox спасибо, идея хорошая, но действительно надо подумать. А надо было сделать быстро ;(
Мне интересно, как менеджер узнаёт, у каких именно блоков нужно дёрнуть
update()
?Он может дергать
update()
у всех блоков, а внутри блок уже проверяет, видим ли он в этот момент. Как-то так: