Цитата:
Цитата:
Цитата:
В моем предствлении это будет так: @set theme_1, theme_2, theme_3 < theme (data) { &.title: data.title } |
Цитата:
<div class="b-me"> ... </div> .b-me { @extend .b-about; } Т.е. у мя тоже всё гуд :) Единственное принципиальное отличие твоего подхода от моего, что ты полностью абстрагируешься от html, заменяя сущность тега, на сущность элемента, а у меня абстракция на уровне блоков, т.е. структура блока всё равно описывается старым добрым HTML: {setBEM b-button, tag: 'span'} {template bButton() extends iBlock} <button class="{this.blockName}__btn"> ... </button> {end} /// Пример вызова: {bem b-button, name: '...', ...}Текст кнопки{end} Но кстати, если у меня меняется именно тема, то я создаю не дочерний блок, а применяю модификатор: b-button_theme_dark и т.д. |
Цитата:
|
Небольшой апдейт: в скрипт трансляции добавлен сборщик файлов Jossy, т.е. если вы компилите шаблоны с помощью консольной команды snakeskin или с помощью фаел вотчера шторма, то вы можете использовать директивы вроде
//#includeдля конкатенации файлов - это может быть очень удобным при разбиении шаблонов на разные файлы. |
У меня тоже есть подобный препроцессор. Правда пока нельзя писать инструкции в виде комментариев )
А чем грант не подошел? |
Цитата:
ЗЫ: понравилась твоя либа для тестирования :) |
Цитата:
|
Цитата:
|
Цитата:
|
kobezzza, у меня возник глупый вопрос: как ты навешиваешь обработчики событий для шаблонизируемых элементов? Через делегирование?
Хочется максимально простого и понятного кода. Допустим, когда я использую ангуляр, я пишу так: <div ng-repeat="item in array"> <!-- повторить див столько раз, сколько элементов в массие --> <div ng-click="controller.click(item)"></div> <!-- навесить обработчик клика на каждый элемент --> </div> // упрощенный js var controller = { click: function(item) { // item - элемент модели, по кот. кликнули } }; можно ли как-нибудь добиться подобного поведения? Так же хочется услышать/увидеть, как ты навешиваешь обработчики с данным шаблонизатором. |
Цитата:
{template bButton.prototype.tpl()} <div class="{this|el 'cont'|elMod 'focus', 'true'}"> </div> {end} Тут фильтры el и elMod и т.д. являются часть моего фреймворка который реализует паттерн БЭМ, т.е. это отдельная хреновина не имеющая отношения к шаблонизатору. Сам Snakeskin - это просто транслятор и микролиба (фильтры + итераторы) и ничего больше. PS: раз уж тут отметился, то скажу, что в декабре хочу запилить новую версию с поддержкой доопределения блоков и прототипов (сейчас есть только переопределение), т.е. что-то вроде: {template page()} {block scripts} <script src="1"></script> <script src="2"></script> {end} {end} {template page2() extends page} {block scripts} {super} /// будет заменено на block scripts из page <script src="3"></script> {end} {end} Сейчас похожее можно сделать с помощью миксина из блоков и прототипов, но выглядит это криво. {template page()} {proto scripts} <script src="1"></script> <script src="2"></script> {end} {block scripts} {apply scripts} {end} {end} {template page2() extends page} {proto page2_scripts} <script src="3"></script> {end} {block scripts} {apply scripts} {apply page2_scripts} {end} {end} |
Цитата:
Цитата:
Цитата:
<div onclick="fn(this)"></div> |
Цитата:
|
Цитата:
|
Цитата:
Шаблон же это простая яваскриптовая функция, которая выплёвывает текст, который затем в innerHTML пихается, т.е. можно делегировать как угодно :) У меня "свои" обработчики пишутся не в шаблоне, а в коде блока. А обработчики для связи 2-х разных блоков в шаблоне как декларативные схемы. PS: а если тебе нужен дата-биндинг почему бы не взять один из уже имеющихся популярных фреймворков, или у тебя спортивный интерес/узкозаточенная задача ? |
kobezzza, спасибо за ответы и терпение :) Форум, как обычно, не плюсует.
|
Цитата:
|
А это должно работать? http://jsfiddle.net/NAPWB/5/
|
Цитата:
{template foo.tpl(name)} <h1 onclick="bar.click(this)">Hello {name}!</h1> {call this.header.tpl(name, 'ещё параметр')} {/template} /// Можно смело использовать неймспейсы любой длины, т.к. Snakeskin создаст их если что {template foo.header.tpl(name, header)} <h2>{header}</h2> <h3>{name}</h3> {/template} НО Для повторного использования кода в шаблоне и его дочерних шаблонах есть прототипы, которые в отличии от вызова шаблона в шаблоне не создают зависимостей, т.е. {template base(name)} {proto foo} 121 {/proto} {apply foo} {apply foo} {/template} Прототип может содержать в себе другие прототипы, блоки и т.д. В дочернем шаблоне можно переопределить и вызвать любой родительский прототип. Прототип не может вызывать сам себя, т.е. {proto foo} {apply foo} {/proto} не получится:) И главное, в скомпилированном шаблоне прототипы никак не фигурируют и не создают никаких связей, т.е. они используются только на этапе трансляции. http://habrahabr.ru/post/168093/ здесь довольно исчерпывающее описание прототипов, хотя сама статья уже местами устарела, т.к. писалась для версии 1 почти год назад, а ща уже 2.4 |
Цитата:
|
На самом деле, для вызова любой функции (а шаблон - это простая функция) можно использовать простой вывод, аля:
{this.header.tpl(name, 'ещё параметр')} Но дело в том, что на все такие выводы по умолчанию ставится фильтр html, который экранирует всякие вредные сущности:) По сути {this.header.tpl(name, 'ещё параметр')|!html} и {call this.header.tpl(name, 'ещё параметр')} эквивалентны, т.к. фильтр !html отменяет действие html, но с call как то получше выглядит |
Оказывается, так нельзя:
<script type="text/x-snakeskin-template" id="templates"> <!-- msg --> {template app.templates.test(data)} <div></div> {/template} </script> на комменты ругается, если я правильно понял |
Цитата:
<script type="text/x-snakeskin-template" id="templates"> /* * Мой супер шаблон */ {template app.templates.test(data)} <div></div> {/template} </script> Однако в след версии я планирую добавить возможность комментов, которые не вырезаются парсером, а вставляются в JS, для того чтобы делать анотации для GCC. А зачем тебе понадобилось вне шаблона что-то делать и как ты это планировал потом использовать? |
Цитата:
<script type="text/x-snakeskin-template" id="templates"> ... </script> использовать много шаблонов. Хотел добавить комментарии к каждому из них. Воспользуюсь таким синтаксисом: Цитата:
|
Цитата:
http://screencast.com/t/iU7AgM6jvjaj *** А по поводу подсветки, в WebStorm можно создать новый формат данных и задать ему подсветку, я так и сделал (но тут надо выносить шаблоны в отдельный файлики). ЗЫ: Если очень нужно, я могу добавит в парсер лексему XML комментариев, как комментарий ВНЕ декларации шаблона, это будет не сложно. |
Цитата:
За видео спасибо :) Цитата:
Цитата:
Цитата:
|
Цитата:
1) npm i -g snakeskin 2) Затем создаёшь новый тип данных в шторме (FileTypes) 3) Потом создаёшь новый вотчер (File Watchers) и настраиваешь его У меня параметры такие: Program: C:\Users\kobez_000\AppData\Roaming\npm\snakeskin.c md Arguments: -s $FileName$ -o $FileNameWithoutExtension$.ss.js Working Directory: $FileDir$ Output Path: $FileNameWithoutExtension$.ss.js Если компилишь шаблоны под ноду, то также нужно поставить флаг -n PS: Вместе со след версией выпущу плагин для grunt |
kobezzza, отлично, спасибо :)
|
Сегодня потратил день, на сортировку скопившихся "хотелок" от новой версии Snakeskin и утвердил следующее:
1) Директива {super}- для безопасной подстановки тела родительского блока или прототипа в указанное место в дочернем. 2) Поддержка параметров для директивы proto Я долго думал над этой фичей, т.к. сама суть прототипов - это "мягкая" декомпозиция шаблона, с поддержкой механизма наследования и безопасной работой, т.е. замена пресловутых вызовов других шаблонов в шаблоне. В общем получается, что сейчас прототипы могут очень ограниченно выполнять свою функцию, т.к. не хватает двух вещей: параметров и поддержки рекурсии. Ожидаемый синтаксис будет таким: {template foo(param)} {proto icon(%iconName)} {param} - {%iconName} {end} {apply icon('foo')} {end} Разумеется, старый синтаксис прототипов (без указания параметров) останется рабочим. 3) Поддержка рекурсий в прототипах. Хотя прототип напоминает простые функции JS, но на самом деле это не так, поэтому использование рекурсии изначально было запрещено, ввиду некорректной работы транслятора (он уходил в бесконечный цикл), но любую рекурсию можно представить в виде простого цикла, собственно что я и планирую сделать для рекурсивных прототипов. 4) Директива для конструкции switch-case-default 5) Директивы {return} {break} и {continue} для прерывания в теле директив циклов и прерывания общего шаблона (return) 6) Директива-хэлпер для безопасной работы с атрибутами узлов: <input {attr 'type', 'text'} /> 7) Декларация прототипов вне тела шаблона: /// Прототип getIcon добавится в конец шаблона foo {proto foo->getIcon} ... {end} 8) Поддержка JSDoc комментариев: комментарии вида /** ... */ не будут вырезаться из шаблона и будут вставлены в конечный JS, чтобы можно было использовать их при дальнейшем сжатии с помощью GCC. В остальном будут исправления ошибок и различные доработки. Завтра приступаю к реализации. |
Здорово, но большинство опций я вряд ли буду использовать. Это уже для продвинутых "юзеров" :)
|
Цитата:
|
В последнее время все больше склоняюсь к выбору шаблонизатора с трансляцией на клиенте.
Кстати вот очень клевая штука: http://www.ractivejs.org/ |
Фух, почти закончил работу над новой версией, вышло дольше чем думал. На этой недели закончу, но уже предлагаю поиграться с бетой и заодно потестить : http://jsfiddle.net/NAPWB/6/.
С функциональной стороны сделано всё, что хотел, и даже больше, однако ещё нужно написать новые тесты, обновить доку и т.д. Ну а теперь по порядку. Изначально данный апдейт планировался как эволюционное развитие ветки 2.x, однако в ходе разработки, стало ясно, что необходимы некоторые принципиальные изменения, которые повлекут за собой несовместимость с предыдущей версией, поэтому новая версия выйдет как 3.0.0. Что изменилось по сравнению с 2.x: 1) Реализована блочная область видимости переменных вместо единой шаблонной, т.е. {template foo()} {if 1} {var a = 1} {a} /// 1 {/} {a} /// error, a is not defined {/} Отмечу, что данное нововведение распространяется именно на переменные, т.е. константы работают также, как и раньше. 2) Переосмыслена и переделана работа с пробельными символами: теперь любой пробельный символ (будь то табуляция или переход строки) трактуются как пробел, однако смежные пробельные символы схлопываются в один, т.е. {template foo()} /// перевод строки даст пробел <div class="foo"> {/} Также введена новая директива &, которая декларирует, что все пробельные символы после этой директивы до любого не пробельного символа будут вырезаться, т.е.: {template foo()} Привет{&} Мир! /// получится ПриветМир! {/} 3) Директивы cut и save были убраны, а их функционал частично заменила новая директива placeholder. Эта директива имеет одинаковый синтаксис с директивой template, однако, в отличии от template, placeholder существует только на этапе трансляции, т.е. {placeholder foo()} foobar {/} foo(); // error Директива может участвовать в цепи наследований также, как и template. 4) Добавлен новый возможный аргумент для директив forEach и forIn (forIn отличается от forEach тем, что всегда итерирует объект, как for (key in data), причём без проверки на hasOwnProperty). Теперь порядок аргументов для forEach такой: Для массивов: *) Ссылка на элемент массива; *) Индекс массива; *) Ссылка на итерируемый массив; *) Является ли первым; *) Является ли последним; *) Длина массива. Для объектов: *) Ссылка на свойство; *) Ключ свойства; *) Ссылка на итерируемый объект; *) Номер итерации; *) Является ли первым; *) Является ли последним; *) Длина объекта. Для forIn аргументы такие же, как и у forEach для объектов. Кстати, теперь директивы forEach и forIn разворачиваются в циклы на этапе трансляции, что позволило увеличить скорость скомпилированных шаблонов, а также возможность использования новых директив break и continue. 5) Для директивы data изменено сокращение, теперь это =, т.е. {template foo()} {= foo, bar} {/} Старое сокращение {{}} теперь используется новой директивой decl, которая вставляет текст, так как он был написан вместе со скобками директивы, т.е.: {template foo()} {{foo}} /// даст {{foo}} {/} Также, как и в data, в decl можно прокидывать переменные из шаблона, с помощью синтаксиса ${}. Новая директива decl создана для удобного использования библиотек, которые осуществляют data binding (вроде http://www.ractivejs.org/). 6) Костыль для console был удалён, теперь нужно использовать директиву void {template foo()} {void console.log(1)} /// или так {?console.log(1)} {/} На этом ломающие изменения заканчиваются. Основные нововведения: 1) Добавлена директива super, которая осуществляет вставку тела родительского блока или прототипа: {template base()} <head> {block scripts} <script src="../1.js"></script> {/} </head/> {/} {template child() extends base} {block scripts} {super} <script src="../2.js"></script> {/} {/} 2) Прототипы теперь поддерживают параметры и рекурсию, а также новую директиву return: {template foo()} {proto bar(i)} {i} {if i === 2} {return} {/} {if i} {apply bar(--i)} {/} {/} {apply bar(4)} /// 4 3 2 {/} 3) Прототипы теперь можно выносить за пределы шаблона, перед декларацией {proto foo->bar} 1 {/} {template foo()} {apply bar} {/} 4) Добавлена поддержка jsDoc /** * Данный комментарий будет в скомпилированном JS * @return {string} */ {template foo()} 1 {/} 5) Директива var теперь имеет сокращение : и поддерживает множественную декларацию {template foo()} {:a = 1} {var b = 2, e = 3} {/} 6) Новые директивы switch case (имеет сокращение >) default {template foo()} {switch 1} {case 1} foo {/} {> 2} foo2 {/} {default} foo3 {/} {/} {/} 7) Новые директивы try catch finally {template foo()} {try} {void dddd()} {catch err} {err} {finally} bar!!! {/} {/} 8) Новые директивы break и continue. Могут использоваться внутри forEach, forIn, for, while, do, repeat. 9) Для цикла repeat-until добавлен псевдоним do-while 10) Новая директива return. Может использоваться внутри прототипов или шаблона (в случае шаблона может принимать значение) 11) Новая директива attr для удобного задания атрибутов узлам в xml разметке: {template foo()} <div {attr 'class', 'foo'} {attr -'bind', 'bar'}></div> /// class="foo" data-bind="bar" {/} 12) Частично переписано АПИ добавления директив, в релизе будет иметь документацию, пример: Snakeskin.addDirective( 'if', { placement: 'template', notEmpty: true }, function (command) { this.startDir(); if (this.isSimpleOutput()) { this.save('if (' + this.prepareOutput(command, true) + ') {'); } } ); Snakeskin.addDirective( 'else', { placement: 'template' }, function () { if (this.structure.name !== 'if') { throw this.error('Directive "' + this.name + '" can only be used with a "if"'); } if (this.isSimpleOutput()) { this.save('} else {'); } } ); 13) Увеличена скорость работы транслятора и исправлено множество ошибок 14) Сильно увеличена скорость работы в "живом" режиме в браузере 15) Очень сильно доработана обработка ошибок 16) 100% code review и рефакторинг + теперь исходный код переписан на ECMAScript 6 :) Вроде всё :) |
Ради интереса сравнил скорость рендеринга с другими шаблонными движками... и каково было моё удивление, что Snakeskin занял первое место, обойдя даже самые примитивные шаблонки http://jsperf.com/templates/2 я почему то был уверен, что результат должен быть другим
|
Цитата:
Цитата:
Цитата:
Цитата:
Цитата:
Цитата:
Цитата:
Цитата:
{template} <script> .... </script> {/template}? Зачем усложнять то? Цитата:
switch text | "one" => 1 | "two" => 2 | => 0 Ну или как LiveScript: switch text case "one" then 1 case "two" then 2 case "three", "four" then "3-4" default then 0 |
Цитата:
Цитата:
Просто в современной разботке single-page приложений перерисовывать весть блок не очень гуд, особенно если в этом блоке сотни и тысячи элементов. |
Цитата:
Цитата:
|
Цитата:
Цитата:
/// Срежет не нужный пробел <p>{&} 1 2 </p> Цитата:
Цитата:
Цитата:
|
Цитата:
Если уж вводить синтаксический сахар, то лично мне кажется нужно отталкиваться от существующих решений. Вот пример из того же Haskell: f x = case x of 0 -> "one" 1 -> "two" _ -> 0 - x Цитата:
Цитата:
Цитата:
Вот к примеру в fest нет ничего лишнего, только основные инструкции и возможность писать произвольный JS код: <fest:script> var obj = {"foo": "bar"}; </fest:script> <fest:each iterate="obj" index="i"> <fest:value>obj[i]</fest:value> </fest:each> Пока я вижу в этом только плюсы, т.к. не нужно придумывать новые инструкции и усложнять парсинг. Цитата:
Цитата:
|
Цитата:
Цитата:
Цитата:
Цитата:
Цитата:
Цитата:
|
Часовой пояс GMT +3, время: 09:23. |