Fest, BEM и data-binding.
<?xml version="1.0"?>
<fest:template>
#!+ <fest:set name="button">
#!+ <div class="button"> боль
<fest:value>params.text</fest:value>
#!- </div>
#!- </fest:set>
</fest:template>
<?xml version="1.0"?> <fest:template context_name="ctx"> #! <fest:include src="buttom.xml"/> #!+ <!-- Динамически подключаем блок по его имени --> #!- <fest:get select="ctx.block">ctx.params</fest:get> </fest:template>
<script src="fest/lego.js"></script>
<script>
#! var template = window.fest["lego"];
#!+ var html = template({
#! block: "button",
#!+ params: {
text: "Click me!"
#!- }
#!- });
</script>
<?xml version="1.0"?> <fest:template> #!+ <div bem:block="button"> <fest:value>params.text</fest:value> #!- </div> </fest:template>
<?xml version="1.0"?> <fest:template context_name="ctx"> #! <fest:include src="buttom.xml"/> #! <fest:include src="icon.xml"/> #! <!-- только include, ничего лишнего --> </fest:template>
<script src="fest/lego.js"></script>
<script>
#! var template = window.fest["lego"];
#!+ var html = template["button"]{
#! text: "Click me!"
#!- });
#! // Ничего лишнего!
</script>
<?xml version="1.0"?>
<fest:template>
<div bem:block="button">
#! <bem:mod name="icon" test="ctx.icon"/>
#!+ <fest:if test="ctx.icon">
#! <bem:icon>{ mods: ctx.icon }</bem:icon>
#!- </fest:if>
<fest:value>params.text</fest:value>
</div>
</fest:template>
Думаю продолжать не нужно и так всё понятно, теперь работа с модифкаторами встроена в сам fest (при помощи системы расширений). Помимо этого, когда сильно прооптемизирован по сравнению с нашим текущим подходом.
<fest:set name="hello"> <div class="hello"> #!+ <h2> #! Hello<:fest:space/> #! <fest:value>ctx.name || "%username%"</fest:value>! #!- </h2> </div> </fest:set>
__fest_blocks.hello = function (ctx) {
#! var __fest_buf = "";
#! __fest_buf += ("<div><h2>Hello ");
#!+ try {
#! __fest_buf += (__fest_escapeHTML(ctx.name || "%username%"))
} catch (e) {
#! __fest_log_error(e.message + "5");
#!- }
#! __fest_buf += ("!</h2></div>");
#! return __fest_buf;
};
var data = { name: "" };
#! var lego = fest["lego"];
#! var el = document.getElementById("playground");
#! el.innerHTML = lego({ block: "hello", params: data });
#! data.name = "Fest";
#! el.innerHTML = lego({ block: "hello", params: data });
Для этого нам необходимо
<div bem:block="hello"> #!+ <h2> Hello<:fest:space/> <fest:value>ctx.name || "%username%"</fest:value>! #!- </h2> </div>
#! var lego = fest.withBindings("lego");
#! var el = document.getElementById("playground");
#! var hello = new fest.ModelView({ text: "" });
#! el.innerHTML = lego["hello"](hello);
#! hello.$set("name", "Fest");
#! hello.$set("name", "Binding");
#! // Красота!
__fest_blocks.hello = function (ctx) {
#! var __fest_buf = "", xid = "-" + __gid++;
#! __fest_buf += ("<div id='"+xid+"'><h2>Hello ");
#!+ /*$V*/ try {
__fest_buf += (__fest_escapeHTML(ctx.name || "%username%"))
} catch (e) {
__fest_log_error(e.message + "5");
} /*V$*/
__fest_buf += ("!</h2></div>");
#!- return __fest_buf;
};
__fest_blocks.hello = function (ctx) {
var __fest_buf = "", xid = "-" + __gid++, __xb = [], __bid;
__fest_buf += ("<div id='"+xid+"'><h2>Hello ");
#! __bid = __xb.push({}) - 1; // добавлям связку
#! __xb[__bid].name = "V"; // название
#!+ __xb[__bid].$render = function () {
#! var __fest_buf = "";
#! try { .. } catch (e) { .. }
#! return __fest_buf;
#!- };
#!+ __fest_buf += "<!--" + __bid + "-->";
// Тут тот же самый код с try-catch, как и в функции $render
#!- __fest_buf += "<!--/" + __bid + "-->";
#! ctx && ctx.$bind && ctx.$bind(xid, __xb);
return __fest_buf;
};
fest.withBindings.add({
#! id: "V", // fest:value
#! dom: "wrap", // обернуть код коментарием для связи
#! render: true, // создать функцию $render
#!+ expr: function (value) {
// Для $render нам больше не нужно экранирование
return value.replace(/\b__fest_escape[^(]+/g, '');
#!- }
});
fest.withBindings.add({
id: "IF", // fest:if
dom: "wrap",
render: true, // всё что внутри if'а
props: { last: '!!__fest_if' },
test: function () { // функция проверки изменений
var __fest_if;
this.getter(); // вместо этого будет вставлен try..catch
if (this.last !== !!__fest_if) {
this.last = !!__fest_if;
return true;
}
}
});
// Идем по всему DOM и связываем комментарии
if (node.nodeType === 8) {
var idx = node.nodeValue;
if (idx.charAt(0) !== "/") {
this.$binds[idx].startEl = node; // начало
} else {
this.$binds[idx.substr(1)].endEl = node; // конец
}
}
// Описываем компонент
fest.ModelView.register("b-dropdown", {
mount: function () { /* в DOM'е */ },
unmount: function () { /* удалил из DOM'а */ },
chooseItem: function (item) {
/* обработка клина на item */
}
});
<div bem:block="b-dropdown"> <div bem:elem="ctrl"><fest:value>ctx.ctrl</fest:value></div> <fest:for-each data="ctx.items" as="item"> <div bem:elem="item" on-click="ctx.chooseItem(item);"> item.text </div> </fest:for-each> </div>