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>