Open uliantsev-a opened 9 years ago
Внутри вашего forEach не надо использовать bemhtml конструкции. Укажите поле cls прямо в bemjson элемента sub-item
Может просто переопределить моду cls у нужного айтема?
block('block').elem('item')(
cls()(function() {
if(this.ctx.items !== null) {
return 'my-class';
} else {
return 'another-class';
}
})
)
сразу в bemjson определить клас к сожалению не получится, т.к. ctx в bemhtml переопределяется и затирает cls из bemjson.
Тут я пытаюсь реализовать типичный блок навигации в шапке, через шаблоны. Взяв за основу пример из руководства про модификаторы: https://github.com/bem/bem-js-tutorial/tree/master/pure.bundles/006-before-set-mod
вот полная версия моего bemhtml:
block('nav')(
tag()('ul'),
js()(true),
content()(function(){
var content = [];
this.ctx.items.forEach(function(item){
var sub = [],
num = 0;
if(item.items != null){
item.items.forEach(function(subItem, i){
sub.push({
elem: 'sub-item',
content: {
elem: 'link',
url: subItem.url,
content: [
subItem.text
]
}
});
});
}
content.push({
elem: 'item',
mods: { current: item.current, disabled: item.disabled },
content: [
{
elem: 'link',
content: [
item.text
]
},
{
elem: 'sub-menu',
content: sub
}
]
})
});
return content;
}),
elem('item')(
tag()('li')
),
elem('link')(
tag()('a'),
attrs()(function(){
var attrs = {};
this.ctx.url && (attrs.href = this.ctx.url);
return attrs;
})
),
elem('sub-menu')(
tag()('ul')
),
elem('sub-item')(
tag()('li')
)
)
bemjson:
{
block: 'menu',
mix: { block: 'header', elem: 'menu' },
content: {
block: 'nav',
items: [
{
url: '#',
title: 'Lean more about BEM',
text: 'Главная',
current: true,
},
{
url: '#',
title: 'Favorite repositories',
text: 'Контакты'
},
{
url: '#',
title: 'Favorite repositories',
text: 'О нас'
},
{
url: '#',
title: 'Favorite repositories',
text: 'Ещё',
items: [
{
text: 'Hotmail',
url: '#'
},
{
text: 'Yahoo',
url: '#'
},
{
text: 'Gmail',
url: '#'
}
]
}
]
}
},
ну наверное придется пока отказаться подобной сложности из-за нехватки опыта с bemhtml =) сделаю просто в bemjson и стилями. К тому же сразу не заметил, такой подход добавлял ненужные вложенные элементы elem('sub-menu').
не могу понять, зачем понадобилось задавать кастомный класс через cls
?
по мелочи: вместо
{
elem: 'link',
content: [
item.text
]
}
можно просто
{ elem: 'link', content: item.text }
@Bumerang47 под bemjson я имел ввиду не файл, а кусок bemjson с элементом sub-item который пушится в коде вашего шаблона. Теперь когда я повнимательнее изучил ваш фрагмент за компом, поясню.
item.cls()('my-class');
не работает, потому что в переменной item
находится кусок исходного bemjson соответсвующего этому item
. Если я правильно понял, то вы зачем то хотите установить кастомный класс для элемента 'item' у которого есть подпункты.
Этого можно достичь следующими способами:
item.cls = 'my-class';
sub.push({
elem: 'sub-item',
content: {
elem: 'link',
url: subItem.url,
content: [subItem.text]
}
})
И правильным, который я сейчас напишу отдельным постом. А вообще хотелось бы услышать ответ на вопрос @veged.
UPD: исправил
Второй вариант, обрабатывать дерево в отдельных шаблонах. Получилось приблизительно так (код не проверял, но должен передать общую идею о принципе):
block('nav')(
tag()('ul'),
js()(true),
content()(function(){
// к каждому пункту item добавить поле elem: item и обработать как bemjson
applyCtx(this.ctx.items.forEach(function(item){
return ctx.extend(item, {
elem: 'item',
mods: { current: item.current, disabled: item.disabled }
});
}));
}),
elem('item')(
tag()('li'),
content()(function(){
applyCtx([
{
elem: 'link',
url: this.ctx.url,
content: this.ctx.text
},
this.ctx.items && {
elem: 'sub-menu',
content: applyCtx(this.ctx.items.forEach(function(subItem){
return ctx.extend(subItem, {
elem: 'sub-item',
mods: { current: subItem.current, disabled: subItem.disabled }
});
}))
}
]);
})
),
elem('link')(
tag()('a'),
attrs()(function(){
var attrs = {};
this.ctx.url && (attrs.href = this.ctx.url);
return attrs;
})
),
elem('sub-menu')(
tag()('ul'),
cls('my-class')
),
elem('sub-item')(
tag()('li'),
content(function(){
applyCtx({
elem: 'link',
url: this.ctx.url,
content: this.ctx.text
})
})
)
)
До сих пор, правда остался небольшой копипаст и в целом шаблон сложно воспринимать.
Вообще данные шаблоны лучше разделить на шаблоны в bemtree и bemhtml. В первых обходить исходное дерево блока nav и генерировать соответсвующие элементы блока в bemjson. А на bemhtml останется непосредственное превращение bemjson в ожидаемый html.
Я делал обход подобной структуры в своём компоненте только на BH
, а не BEMHTML
.
Получилось тоже довольно громоздко, т.к. не используются плюсы двухпроходной шаблонизации.
Совсем скоро я его переделаю на bemtree + bemhtml.
Спасибо за ответы, сейчас изучу, протестирую код подробней.
По поводу вопроса @veged, хочу добавить, как верно предположил @Guria, для элемента 'item' у которого есть подпункты стиль, выделяющий его среди прочих подпунктов.
@Bumerang47 Если я правильно понял кейс, то такой класс правильнее задавать через модификатор. Свойство cls
существует для гибкости и, если хотите, математической полноты, но используется крайне редко, в случаях, когда нет возможности использовать модификаторы или миксы — например, для работы с какими-то плагинами, которые используют кастомные классы, и которые нет возможности поправить. В общем случае использование cls
не рекомендуется, как и изменять DOM напрямую.
@zxqfox, верно подметили. Миксы и модификаторы скорее больше подойдут, пробовал их применять, написал про cls, т.к. он был как последняя попытка, более грубого приёма. =\ Думаю от этого суть не сильно изменится, если получиться назначить cls, можно будет заменить на mix?
@veged, спасибо за подсказки "по мелочи", это важно :)
@Guria,
первый метод про
item.cls = 'my-class'; sub.push({
результата не дает, класс не добавляется. Судя по всему т.к. в этом блоке суть в переменной sub, которая потом через json подставляется в контент ко всем item.
Концепт второго варианта мне очень понравился, попробую его развить. Сейчас этот код вызвал ошибку "AssertionError: mode literal predicates can't have arguments", к моему стыду пока не могу понять почему. Подобная проблема была когда контент не был обернут в функцию, но тут везде как надо.
@Bumerang47 Да, суть, как раз, останется той же, но появится некоторый простор для маневра. Кастомные классы сильно сложнее шаблонизировать, сложнее слушать их изменения, и пр. пр.
@Bumerang47 в моём примере ошибка в использовании cls. Надо заменить на: cls()('my-class')
. А лучше на соответствующие mods или mix.
@Guria cls()('my-class') не решили ошибку, если речь о втором примере. Ошибку вызывает что-то тут кажется (могу ошибаться):
applyCtx(this.ctx.items.forEach(function(item){
return ctx.extend(item, {
elem: 'item',
mods: { current: item.current, disabled: item.disabled }
});
}));
у меня главный вопрос — зачем вообще городить всё это с преобразованием кастомного json в БЭМ-термины? почему бы просто не написать примерно так:
{
block: 'menu',
mix: { block: 'header', elem: 'menu' },
content: {
block: 'nav',
content: [
{
elem: 'item',
mods: { current: true },
url: '#',
title: 'Lean more about BEM',
content: 'Главная',
},
{
elem: 'item',
url: '#',
title: 'Favorite repositories',
content: 'Контакты'
},
{
elem: 'item',
url: '#',
title: 'Favorite repositories',
content: 'О нас'
},
{
elem: 'item',
url: '#',
title: 'Favorite repositories',
content: 'Ещё',
},
{
block: 'sub-menu',
content: [
{
elem: 'sub-item',
content: 'Hotmail',
url: '#'
},
{
elem: 'sub-item',
content: 'Yahoo',
url: '#'
},
{
elem: 'sub-item',
content: 'Gmail',
url: '#'
}
]
}
]
}
}
и дальше простые шаблоны, без всякого ада с applyCtx
и подменами какими-то не прозрачными
если говорить, почему не работает applyCtx(this.ctx.items.forEach(function(item){
, то потому, что в applyCtx
нужно передавать новый контекст, а функция forEach
ничего такого не возвращает — нужно её заменить на map
(во втором использовании this.ctx.items.forEach
тоже)
@veged откровенно говоря до меня после нескольких постов этой темы дошло, что тут попахивает избыточным подходом. Да и компонент я делал на основе примера из руководства, задачи которых немного разные. Хотя нагородив в шаблоне bemhtml, можно минимизировать код в файле.bemjson, удобно для быстого конфигурирования блока в будущем на других проектах... но это не было конечно целью и притянуто за уши. Так что вы скорее правы :)
@Bumerang47 Он же дедушка @veged, конечно он прав ;-)
Здравствуйте, подскажите пожалуйста по следующией конструкции bemhtml. Элементу item, из условия необходимо добавить произвольный класс. Но не могу понять доступную конструкцию обрабатываемого элемента. Вызывается ошибка "TypeError: string is not a function" Пробовал item.cls()('my-class') и item.ctx.cls()('my-class') и this.. Но кажется что-то я совсем не то делаю.