Javascript-форум (https://javascript.ru/forum/)
-   Ваши сайты и скрипты (https://javascript.ru/forum/project/)
-   -   Шаблонизатор aTemplate (https://javascript.ru/forum/project/37127-shablonizator-atemplate.html)

FINoM 08.04.2013 17:23

Шаблонизатор aTemplate
 
Сегодня расскажу о шаблонизаторе, который я сделал несколько месяцев назад, но немного завис с документацией. Шаблонизатор успешно используется в нескольких проектах и работает на ура, перетерпевая только самые незначительные изменения. В качестве объяснения процитирую текст из первой версии документации, где я противопоставлял jQuery.tmpl и EJS со своим (этот текст будет выпилен, так как во-первых не очень красиво получается, во-вторых есть куча альтернатив).

---------
Шаблонизатор aTemplate — модификация микро-шаблонизатора Джона Резига http://ejohn.org/blog/javascript-micro-templating/ с расширенным функционалом. Целью aTemplate является создание шаблонизатора, лишенного минусов самых популярных на сегоднышний день шаблонизаторов jQuery.tmpl и EJS.

Что умеет aTemplate?
1. Делать всё, что оригинальный шаблонизатор Джона Резига. Синтаксис шаблонов аналогичен шаблонизатору EJS.
Как известно, jQuery.tmpl имеет собственный синтаксис шаблонов, далекий от оригинального JS кода, поэтому его скорость меньше. Кроме этого, у людей, использующих jQuery.tmpl часто возникают трудности из-за этого: сделать что-либо, выходящее за рамки шаблонизатора, например, получить индекс текущей итерации либо невозможно, либо очень сложно. aTemplate, как и EJS лишен этого минуса, так как в нем используется самый обычный Javascript без заморочек создателя.

2. Перебирать элементы с помощью each, который наличествует в jQuery.tmlp, но отсутствует в EJS. Часто разработчику приходится перебирать набор данных из массива или объекта, и, в случае с EJS, приходится использовать некрасивый for:
<% for( var i = 0; i < users.length; i++ ) { %>
	<span><%= users[ i ].name %></span>
<%} %>

или
<% for( var i in users ) if( users.hasOwnProperty( i ) ) { %>
	<span><%= users[ i ].name %></span>
<%} %>

или
<% for( var i = 0; i < users.length; i++ ) with( users[ i ] ){ %>
	<span><%= name %></span>
<%} %>

Вместо
{{each users}}
	<span>${name}</span>
{{/each}}

Второй вариант удобнее, правда?
aTemplate делает это так (хотя синтаксис EJS тоже сработает):
<%#each( users )%>
	<span><%= name %></span>
<%#/each%>


Причем, поддерживаются как массивы, так и обычные объекты.

3. Привязывать данные к элементу. Часто есть нужда привязать данные к элементу, генерируемому шаблонизатором. Такого функционала нет ни в jQuery.tmpl ни в EJS.

4. Работа в качестве jQuery плагина. Не пугайтесь, aTemplate можно использовать и без этой библиотеки.

Что не умеет aTemplate
Делать ajax запросы для получения шаблонов. Автору такой функционал показался излишеством. При желании программист, использующий aTemplate, может добавить его самостоятельно.

В итоге мы получаем:
1. Скорость, сравнимую с шаблонизатором Джона Резига
2. Гибкость, как в EJS
3. Удобство, как jQuery.tmpl

--------

Конструкция <%#each%> может содержать псевдоаргументы: перебираемый объект, текущий элемент итерации, ключ.
<%#each( users, user, index )%>
...
<%#/each%>

По умолчанию эти псевдоаргументы следующие:
this, _item, _key
Это значит, что можно не определять все три.
Написав:
<%#each%>
...
<%#/each%>

Получаем то же самое, что и:
<%#each( this, _item, _key )%>
...
<%#/each%>



Шаблонизатор запускается так:
aTemplate({
	template: 'templatetext',
	data: data
});


Поддержка карринга сохранена:
aTemplate({
	template: 'templatetext'
})( data );


Вместо template можно указать name:
aTemplate({
	name: 'name'
})


При этом, template будет браться из элемента (div, script...) с атрибутом data-atemplate. Я предпочитаю использовать script:
<script type="text/atemplate" data-atemplate="mytemplate">...</script>



По поводу работы в качестве jQuery плагина:
$( '#mytemplateblock' ).aTemplate( data );

или
$( '#mytemplateblock' ).aTemplate()( data );


Где '#mytemplateblock' - блок, в котором находится шаблон, например:
<div id="mytemplateblock">...</div>




Я всегда испытывал проблемы с тем, как же привязать объект к ноде в текстовом шаблоне. Привязка данных — это очень простая фича, которой мне всегда не хватало.

Раньше приходилось извращаться вот так:
<% for( var i = 0; i < this.length; i++ ) { %>
	<div data-name="<%= this[i].name %>" data-email="<%= this[i].email %>" data-age="<%= this[i].age %>" ... ><%= this[i].name %></div>
<%}%>


Здесь это делается так:
<%#each( this, user )%>
	<div data-abind="<%= at.bindData( user ) %>"><%= name %></div>
<%#/each%>




Вытащить данные из элемента можно так:
aTemplate.getBindedData( node );

или по айдишнику, который генерируется функцией at.bindData:
aTemplate.getBindedData( id );


Людям, использующим jQuery будет особенно удобно извлекать данные:
$( element ).data( 'aTemplate' );

или так:
$( element ).data().aTemplate;

или вот так:
$( element ).aTemplateData();


Несмотря на краткость кода, о функционале можно писать очень много, поэтому я могу забыть что-то упомянуть.

Ах, да, для тех, кто использует EJS и его небольшое дополнение view.js, последний был портирован для aTemplate:
<script type="text/atemplate">
	link_to( name, href );
 </script>


(честно признаюсь, тестирование всех функций view.js не проводилось)

https://github.com/finom/aTemplate
В репозитории только код. В документации расчитываю показать все возможные применения (пока не могу сказать, когда это случится). Несмотря на это, шаблонизатор уже можно юзать. Код очень простой (я надеюсь), поэтому, легко разобраться, как работает тот же each или привязка данных.

kobezzza 08.04.2013 21:07

1) Вопрос скорости трансляции не принципиален, т.к. любой нормальный шаблонизатор поддерживает предварительную трансляцию в js (т.е. на этапе сборки проекта в отдельный файлик) и во всех крупных проектах используется именно такой подход.
2) Имхо далеко не самый удачный синтаксис.
3) Отсутствие системы повторного использование кода.

Резюме: для маленьких и средних проектов сойдёт, для больших и тем более огромных - увы, такие шаблоны превращаются в месиво: поддерживать конечно можно, но это жесть.

Ну а так за велосипед респект :)

FINoM 08.04.2013 23:51

Цитата:

Сообщение от kobezzza
далеко не самый удачный синтаксис.

Это JS, всякие синтаксические велосипеды довольно часто вызывают кучу проблем.
Цитата:

Сообщение от kobezzza
Отсутствие системы повторного использование кода.

Например?
Цитата:

Сообщение от kobezzza
для маленьких и средних проектов сойдёт, для больших и тем более огромных - увы

Ну тогда не знаю. Пожалуй, я работаю только над маленькими и средними проектами.

kobezzza 09.04.2013 08:45

Цитата:

Сообщение от FINoM (Сообщение 244921)
Это JS, всякие синтаксические велосипеды довольно часто вызывают кучу проблем.

Да норм всё. А вот кучи проблем с твои синтаксисом я могу назвать:

1) Захотим мы переписать шаблонизатор к примеру на пхп, чтобы использовать на сервере, а потом обнаружим, что в шаблоне используются очень специфичные JS конструкции и огребём гемора. Шаблонизатор не должен уметь много - это скорее недостаток, чем плюс.

2) Пришёл новый верстальщик в проект: он не знает JS и ему трудно вкурить твой синтаксис.

3) Когда размер шаблона начинает превышать n количество строк то код превращается в месиво, особенно с таким синтаксисом.

4) Лучше просто, чем сложно.

Цитата:

Сообщение от FINoM (Сообщение 244921)
Например?

Вот есть у тебя шаблон А и есть B. И так вышло, что тело шаблона B включает в себя тело A, что делать: первое что приходит в голову, это вызвать шаблон в шаблоне, но такое решение слишком прямое (и порождает дополнительные связи), т.к. в реальной жизни нам часто нужно не просто включить тело шаблона, но и передекларировать часть его структуры и в твоём случае это закончится копипастой, а была бы поддержка наследования шаблонов, то данная проблема решилась элегантно и просто.

Пример, как это можно сделать:
https://github.com/kobezzza/Snakeskin#--3
http://habrahabr.ru/post/168093/

Цитата:

Сообщение от FINoM (Сообщение 244921)
Ну тогда не знаю. Пожалуй, я работаю только над маленькими и средними проектами.

К сожалению всё приходит с опытом.

FINoM 09.04.2013 22:45

Цитата:

Сообщение от kobezzza
Захотим мы переписать шаблонизатор к примеру на пхп, чтобы использовать на сервере, а потом обнаружим, что в шаблоне используются очень специфичные JS конструкции и огребём гемора.

Как вариант, вместо шаблонизатора, можно переписать шаблоны (не более сложная задача). Да и юзайте node.js :)
Цитата:

Сообщение от kobezzza
Шаблонизатор не должен уметь много - это скорее недостаток, чем плюс.

Очень спорно. Люди успешно юзают EJS и не парятся.
Цитата:

Сообщение от kobezzza
Пришёл новый верстальщик в проект: он не знает JS и ему трудно вкурить твой синтаксис.

А вкурить синтаксис другого шаблонизатора проще?
Цитата:

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

Тоже спорно. Шаблоны, как правило, используют всего лишь each и иногда методы (.toFixed и пр.). В таком случае, шаблон на том же jQuery.tmpl будет ненамного отличаться от моего.
Цитата:

Сообщение от kobezzza
Лучше просто, чем сложно.

Полностью согласен :)
Цитата:

Сообщение от kobezzza
Вот есть у тебя шаблон А и есть B. И так вышло, что тело шаблона B включает в себя тело A

В таком случае, я просто компилирую родительский шаблон, потом вставляю скомпилированные дочерние блоки. В моих проектах всегда есть метод .render (как и в тех, которые написаны на том же backbone), так что нужно просто определить в JS, как рендерить блок.

Ну и да, по поводу синтаксиса: твой синтаксис довольно сложен для верстальщика (имхо, конечно же), но в случае с тем же EJS, изучение синтаксиса, то есть обычного JS, ему будет только на пользу.
Цитата:

Сообщение от kobezzza
К сожалению всё приходит с опытом.

Это был сарказм.

Ты, конечно, прав, но вопрос не только в опыте, но и в том, какие задачи ты решаешь. Это очень хорошо, что ты решаешь определенную задачу, а не пишешь сферического коня (я надеюсь). Но, появись иная задача, и в твоём шаблонизаторе найдется какая-нибудь проблема, которая не решаема внутренними методами.

Лично мне никогда не приходилось перетаскивать шаблоны на сервер. Поэтому, твоё замечание об этом я расцениваю, как вполне реальную, но редкую задачу, с которой я, возможно, никогда не встречусь, не зависимо от стажа и опыта.

devote 09.04.2013 23:25

Цитата:

Сообщение от FINoM
Лично мне никогда не приходилось перетаскивать шаблоны на сервер.

Цитата:

Сообщение от FINoM
как вполне реальную, но редкую задачу

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

FINoM 10.04.2013 00:07

Цитата:

Сообщение от devote
займешься высоко нагруженными проектами, столкнешься с переносом на сервер. В таких проектах обычно идет пре-компиляция шаблонов, на уровне выкладки, пред подготовка перед запуском.

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

nerv_ 10.04.2013 12:09

Цитата:

Сообщение от kobezzza
Шаблонизатор не должен уметь много - это скорее недостаток, чем плюс.

золотые слова :)

Цитата:

Сообщение от kobezzza
Лучше просто, чем сложно

"лучше меньше, да лучше" )


Не скажу, что знаток шаблонизаторов, но кое-что сказать могу. Я пользуюсь http://aefxx.com/api/jqote2-reference/ Не то чтобы супер-гуд, но тем не менее.

1. Хеширование шаблонов как лямбда функций
2. Удобный синтаксис вызова
3. В большинстве случае нет нужды в
Цитата:

Сообщение от FINoM
<%#each( this, user )%>
    <div data-abind="<%= at.bindData( user ) %>"><%= name %></div>
<%#/each%>

подобных конструкциях, т.к. шаблонизатор сам итерирует, достаточно только передать массив. Пример (т.е. массив будет развернут автоматически, этого более чем достаточно):
<script type="text/x-jqote-template" id="template-timeline-ruler">
    <div class="timeline-ruler-pick" style="width:<%=this.width%>px;">
        <%=this.value%>
    </div>
</script>

4. Текущий объект итерации доступен как this.
5. Наследования шаблонов нет (вроде как)

FINoM 14.04.2013 19:21

Цитата:

Сообщение от nerv_
Хеширование шаблонов как лямбда функций

Объясни, пожалуйста, что такое хеширование шаблонов (и зачем оно нужно) и что в данном контексте значит "лямбда функция". Если я всё правильно понял, ты немного опечатался (не хеширование, а кеширование), и эту задачу решает карринг, который наличествует в шаблонизаторе Резига и сохранён в моём.
Цитата:

Сообщение от nerv_
Текущий объект итерации доступен как this.

ХЗ, не вижу в этом смысла, имея переменную _item (которую можно переопределить). Вызывать call/apply не хочется.
Цитата:

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

Ну не знаю... Почему тогда не итерировать объект.

nerv_ 15.04.2013 21:59

Цитата:

Сообщение от FINoM
ты немного опечатался (не хеширование, а кеширование)

да. Я набирал текст в хроме, он выделил слово красным и предложил исправить. Я исправил, результат ты видишь :)

Цитата:

Сообщение от FINoM
Ну не знаю... Почему тогда не итерировать объект.

может и итерирует, я не проверял. Массив - интуитивно.

Цитата:

Сообщение от FINoM
не вижу в этом смысла

this пишется проще и глазу приятнее )

Цитата:

Сообщение от FINoM
и что в данном контексте значит "лямбда функция"

насколько я понял, этим термином автор плагина называет функцию, полученную в результате работы шаблонизатора (та, кот. генерит разметку)


Часовой пояс GMT +3, время: 15:18.