bem-site / bem-forum-content-ru

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

Мода js в bemtree #170

Closed apsavin closed 9 years ago

apsavin commented 9 years ago

Привет.

Я тут думаю, как бы мне так доопределить bemtree, чтобы можно было писать

block('block').js()({a: 1});

и

block('block')(
    js()({a: 1})
);

вместо

block('block').def()(function () {
   this.ctx.js = {a: 1};
   return applyNext(this.ctx);
});

Может, кто-то уже делал такое?

qfox commented 9 years ago

Вот этот applyAsync не тот же механизм, который ты ищешь?

apsavin commented 9 years ago

нет.

qfox commented 9 years ago

Тогда я бы копал отсюда

apsavin commented 9 years ago

Решил задачу, добавив на своем уровне переопределения:

def()(function () {
    var content = apply('content');
    if (content || content === 0) {
        this.ctx.content = apply('', { ctx: content });
    }
    var js = apply('js');
    if (js) {
        this.ctx.js = js;
    }
    return this.ctx;
});

js()(undefined);

Может кто-нибудь прокомментировать, насколько это плохо? Почему бы это не включить в bemtree?

qfox commented 9 years ago

:+1: Насколько я понимаю ситуацию, то если и включать, то в bem-core/i-bem.bemtree, я лично за, выглядит очень логично. Наверное, надо позвать @veged и @mishanga

voischev commented 9 years ago

@apsavin я не понимаю зачем ты хочешь в шаблоне bemtree для конкретного по моде записывать в js что-то? Мне кажется ты хочешь не правильного.

У тебя же всегда есть какой нибудь блок page.bemtree в котором ты описываешь бэмдерево вот там самое место что бы в bemjson для внутренних блоков описать все что ты хочешь передать в js. Надеюсь я понятно написал)

apsavin commented 9 years ago

@voischev я хочу, чтобы блок сам ходил за данными, которые ему нужны.

voischev commented 9 years ago

@apsavin не знаю какая у тебя специфика, но у нас такой подход не очень прижился, потому что получается очень много лишних преобразований и дублирований. В наших проектах удобнее формировать дерево страницы из одного места, ну и соответственно прикидывать нужные данные в блоки из этого же места). У нас одни и те же блоки могут принимать структурно похожие данные из разных мест и кажется очень сложно будет накрутить такую логику внутри блока, который по сути должен будет знать про все приложение, или все возможные варианты использования этого блока, вместо того что бы просто принимать данные и что-то с ними делать внутри.

qfox commented 9 years ago

@voischev у меня есть ощущение, что должно быть два класса блоков: страничные, и обычные. И у страничных можно кроме bemtree, с общей канвой внутренней структуры, описать еще и нодовую технологию, которая будет за данными бегать, а в обычных блоках только bemtree при необходимости, ну или, если блок прям совсем независимый, тоже можно нодовую реализацию. Таким образом у нас получается куча разных типов страниц, описанных страничными блоками, которые знают примерно что в них напихано, и знают чем это рисовать, а дальше логика всем известная: bemtree→bemjson→bemhtml.

upd: т.е., рутовых блоков для bemtree может быть не один, а пачка. А какой из них нужный для конкретной страницы — решится при запуске нодовой технологии. У нас такой подход (на php аналоге node+bemtree) идеально ложится под CMS и нужды.

voischev commented 9 years ago

Я не спорю. Но мне думается что таких блоков не должно быть много :) рутовый блок по дефолтной ноде обычно вернет другой контекст с другим блоком. Вот и для решения этой задачи я бы сделал блок без представления который бы готовил данные для блока который нужно отобразить. Будет выглядить гораздо красивее ;)

apsavin commented 9 years ago

// cc @narqo

tadatuta commented 9 years ago

@apsavin Если я правильно понимаю, то достаточно на своем уровне положить

def()(function() {
    this.ctx.js = apply('js');
    return applyNext();
});

PS: Для твоей задачи, когда каждый блок ходит за своими данными, обрати внимание на https://github.com/baby-loris/bla — модуль как раз для этого.

apsavin commented 9 years ago

@tadatuta То, что ты предлагаешь - это то, с чего я начал, в такой записи apply('js') ничего не возвращает, помогает только редактирование i-bem.bemtree в bem-core так, как я написал выше. Но, может быть, я где-то ошибся, попробую еще раз.

Спасибо за рекомендацию bla, но у меня уже свой велосипед, который делает примерно тоже самое, но более удобным для меня образом.

qfox commented 9 years ago

@tadatuta Я вчера пытался найти в доке, в тестах что-то похожее, но так и не понял, как это сделать, пока не увидел этих четырех строчек. А кейс, по ощущениям, частый.

Есть же еще вариант реализовывать это через mode('custom-mode')(...), верно? Чем он хуже «стандартных» мод типа js? Почему нельзя доопределить стандартные моды на базовом уровне (или все же можно)? (Имеется ввиду, определение некоегово aria, например, с последующим использованием aria('flowto')(...);)

Ощущение, что написано про это много, всё в примерах, но что-то важное упущено.

veged commented 9 years ago

@apsavin apply('js') не будет ничего возвращать для случаев, когда на него не написаны шаблоны

надо или добавить к коду @tadatuta

js()(function() { return this.ctx.js })

или делать примерно как в твоём изначальном коде:

def()(function() {
    var js = apply('js');
    typeof js !== 'undefined' && (this.ctx.js = js); // проверять undefined, чтобы можно было делать block('b1').mod('m1', 'v1').js()(false)
    return applyNext();
});
veged commented 9 years ago

в базовые шаблоны не хочется это выносить, т.к. обычно всё можно сделать в моде def

tadatuta commented 9 years ago

@zxqfox любые моды до- и переопределяются совершенно консистентно, хоть базовые, хоть кастомные. и в этом плане пример про доопределение моды content с вызовом applyNext() ничем не отличается от аналогичного доопределения дефолтной моды

qfox commented 9 years ago

@tadatuta Вот это как раз и неочевидно. Пример такого рода был бы весьма кстати.

apsavin commented 9 years ago

@veged @tadatuta Странно, но предложенные вами варианты не работают. А вот если мой вариант разместить на уровне проекта в i-bem.bemtree - то все ок. Не работают - это значит то, что я передаю в js, игнорируется.

apsavin commented 9 years ago

@veged apply('js') всегда возвращает undefined в случае использования

def()(function() {
    var js = apply('js');
    typeof js !== 'undefined' && (this.ctx.js = js); // проверять undefined, чтобы можно было делать block('b1').mod('m1', 'v1').js()(false)
    return applyNext();
});

и вызывается явно меньшее количество раз. Собственно, видимо, просто не вызывается для того случая, когда я его использую.

tadatuta commented 9 years ago

@apsavin я взял project-stub@bem-core, сделал https://github.com/bem/project-stub/compare/bemtreeJsMode?expand=1 (т.е. ровно такой шаблон, как в твоем комменте, без отдельного объявления моды js, как рекомендует @veged ) и в результате получаю:

[ { block: 'b1', mods: {}, js: true },
  { block: 'b2', mods: {}, js: { f1: 1, f2: 2 } },
  { block: 'b3', mods: {}, content: 'has no js mode' } ]

Чтобы повторить:

git clone https://github.com/bem/project-stub.git -b bemtreeJsMode test
cd $_
npm i
enb make
node test
apsavin commented 9 years ago

@tadatuta

node test

evalmachine.<anonymous>:180
    throw e;
          ^
Error: Match failed
    at run (evalmachine.<anonymous>:170:11)
    at BEMContext.<anonymous> (evalmachine.<anonymous>:114:14)
    at bodyHandler (evalmachine.<anonymous>:92:54)
    at apply (evalmachine.<anonymous>:113:40)
    at bemApply (evalmachine.<anonymous>:347:20)
    at BEMContext.<anonymous> (evalmachine.<anonymous>:558:14)
    at run (evalmachine.<anonymous>:173:15)
    at BEMContext.<anonymous> (evalmachine.<anonymous>:114:14)
    at bodyHandler (evalmachine.<anonymous>:92:54)
    at apply (evalmachine.<anonymous>:113:40)

Не может ли быть дело в

echo $BEMTREE_ENV
development

???

tadatuta commented 9 years ago

@apsavin для dev-режима лечится так:

// i-bem.bemtree на уровне проекта
js()(undefined); // добавилась эта строка

def()(function() {
    var js = apply('js');

    typeof js !== 'undefined' && (this.ctx.js = apply('js'));

    return applyNext();
});
apsavin commented 9 years ago

Да, но если использовать дерево

[
    {
        block: 'b1',
        content: {
            block: 'b2'
        }
    },
    {
        block: 'b2'
    },
    {
        block: 'b3'
    }
]

то с моим кодом результат:

[ { block: 'b1',
    content: { block: 'b2', mods: {}, js: [Object] },  //ко вложенному блоку применилось правило js
    mods: {},
    js: true },
  { block: 'b2', mods: {}, js: { f1: 1, f2: 2 } },
  { block: 'b3', mods: {}, content: 'has no js mode' } ]

а с вашим:

[ { block: 'b1',
    content: { block: 'b2', mods: {} }, // вложенный блок остался без js
    mods: {},
    js: true },
  { block: 'b2', mods: {}, js: { f1: 1, f2: 2 } },
  { block: 'b3', mods: {}, content: 'has no js mode' } ]
tadatuta commented 9 years ago

@apsavin, действительно, applyNext() выставляет защиту от зацикливания в виде поля в this. И т.к. this общий для всех потомков, то вглубь по дереву шаблон больше не матчится.

apsavin commented 9 years ago

Угу, видимо, придется оставить мой вариант, с некоторым дублированием кода из i-bem.bemtree в bem-core