bem-site / bem-forum-content-ru

Content BEM forum for Russian speak users
MIT License
56 stars 6 forks source link

Как сделать, чтобы шаблон блока не менял в контексте поле block #842

Open adinvadim opened 8 years ago

adinvadim commented 8 years ago

На выходе после BEMTREE шаблонизации выходит примерно такой bemjson

{
    block: 'video'
    content: [
        {
            elem: 'media',
            urls: data.links,
        },
        {
            elem: 'content',
            content: [
                {
                    elem: 'title',
                    content: data.title
                },
                {
                    elem: 'description',
                    content: data.description
                }

            ]
        }
    ]
}

Элемент __media с помощью bemhtml потом оборачивается в блок wrapper-block Что нужно написать в блоке wrapper-block, чтобы он не менял в контексте block, чтобы не делать вот так:

{
    block: 'video'
    content: [
        {
            block: this.block,
            elem: 'media',
            urls: data.links,
        },
        {
            block: this.block,
            elem: 'content',
            content: [
                {
                    block: this.block,
                    elem: 'title',
                    content: data.title
                },
                {
                    block: this.block,
                    elem: 'description',
                    content: data.description
                }

            ]
        }
    ]
}
tadatuta commented 8 years ago

@adinvadim

bem-xjst работает таким образом, что если поле block не задано явно, то оно обязано быть достроено из контекста. И если текущий контекст оказывается wrapper-block, то остается лишь явно задавать нужный блок самостоятельно.

Другое дело, что это можно очень по-разному выразить в коде. Например:

[
        {
            elem: 'media',
            urls: data.links,
        },
        {
            elem: 'content',
            content: [
                {
                    elem: 'title',
                    content: data.title
                },
                {
                    elem: 'description',
                    content: data.description
                }

            ]
        }
].map(function(item) {
    item.block = this.block;
    return item;
}, this);

Либо вместо оборачивания в wrapper-block завернуть в video__inner, смиксованный с wrapper-block, тогда сохранится контекст video.

adinvadim commented 8 years ago

О, с video__inner и правда крутое решение, спасибо! А еще такой вопрос, у меня многие блоки могут иметь или не иметь обертку wrapper-block и поэтому у многих пристуствует такой код, можно как-нибудь избежать большого повторения кода и написать что-то типа wrapped()(true)?

    def()(

        match(function() { return !this.ctx._wrapped })(function() {
            this.ctx._wrapped = true
            return applyCtx({ block: 'wrapper-block', content: this.ctx })
        })

    ),
tadatuta commented 8 years ago

Для оборачивания из коробки есть хелпер:

block('b1').wrap()(function() {
    return {
        block: 'wrapper-block',
        content: this.ctx
    };
});

Это отвечает на вопрос или хочется чего-то другого?

adinvadim commented 8 years ago

Ого, круто, а много еще подобных хелперов, которых в доках нет, или я где-то не досмотрел?

Хочется уметь самому хелперы писать, где можно про это почитать?

tadatuta commented 8 years ago

Есть еще replace() для замены блока новым деревом (чаще пригождается в BEMTREE):

block('b1').replace()(function() {
    return [{ block: 'b2' }, { block: 'b3' }];
});

По поводу своих хелперов. Есть два варианта: положить хелпер в this (будет доступен из всех блоков) или написать кастомную моду для селектора на *:

// добавляем хелпер в this
oninit(function(exports) {
    exports.BEMContext.prototype.someHelper = function() {
        // ...
    };
});

// использование
block('b1').content()(function() {
    return 'blah' + this.someHelper();
});

// кастомная мода для любых блоков
block('*').mode('someMod')(function() {
    return 'something';
});

// ее можно звать вручную, когда реально требуется или дергать из `def()` автоматически для всех:
block('*').def()(function() {
    apply('someMod');
    return applyNext();
});
adinvadim commented 8 years ago

:fire: Спасибо большое за развернутый ответ!

adinvadim commented 8 years ago

у меня с этой проблемой появился еще один кейс, и не могу пока что найти нормальное решение

есть струкура:

.block (+ модификаторы)
  ...
  .block__preview
    .block__overlay
    .block__image
  ...

Потом bemhtml оборачивает block__preview в блок link из bem-components, и соотвественно меняется контекст дальше и получается такой результат

.block (+ модификаторы)
  ...
  .link
    .link__preview
      .block__overlay
      .block__image
  ...

block: this.block решает только ряд проблем, потому что помимо поля block в контексте изменилось еще поле mods

block: this.block и mods: this.mods решает проблемы, но это плохое решение, так как все модификаторы выставляются на элементы, что немного странно, по идеи чтобы повесить модификатор на элемент, нужно воспользоваться elemMods

и получается что-то типо этого:

.block (+ модификаторы)
  ...
  .link
    .block__preview (+ модификаторы блока block)
      .block__overlay
      .block__image
  ...

Возможно тут как-то можно доопределить шаблон link, но у меня идей нет

tadatuta commented 8 years ago

Можно явно сбросить mods в undefined.

Но точно ли нужна такая «магия» внутри BEMHTML? Я бы предложил строить полное дерево уже на этапе BEMTREE, а для BEMHTML оставить чисто «визуальные» штуки: кому какой тег задать, атрибуты и т.п.

PS: относительно контекстное обсуждение сегодня: https://github.com/bem/bem-forum-content-ru/issues/848 (в комментариях есть вариант решения с wrap()).

adinvadim commented 8 years ago

Тогда тут можно типо того сделать ? (пример)

Ибо чтобы проверять с помощью матчера на модификатор (пример), нужно проделать неприятные костыли для элементов

...
block: this.block,
mods: this.mods
...

и в шаблоне потом this.ctx.mods = undefined что может потом в даленейшем попортить жизнь

tadatuta commented 8 years ago

Я бы в данном случае скорее сделал бы шаблон для media-item__link копипастом из link (к нему, если нужны стили link, можно добавить микс с link). Тогда контекст сохранится. Кмк, это будет меньшим из зол.