Рекурсивные mustache-темплейты и SMTRails
Последнее время я очень полюбил mustache-темплейты и использую их всегда, когда нужно отрисовать кусок HTML на сервере и потенциально на клиенте. Причем я не хочу переходить на более продвинутый handlebars, так как мне кажется, что logicless-сущность mustache помогает создать более изящную архитектуру, лучше спроектировать json'ы общения клиента и сервера и избежать соблазна сделать erb-кашу из шаблонов.
Mustache я начал использовать после прошлогоднего ролика Райана
Sharing Mustache Templates и делал
точно по рейлскасту: подключал вручную все паршиалы, качал mustache.js и т.д. Это немного муторно, но все поменялось,
после того как Алексей создал прекрасный гем smt_rails.
Про smt_rails
уже писали несколько раз, смотрите например
статью Михаила Бортника, поэтому повторять зачем он нужен
я не буду, лучше расскажу про mustache-паршиалы и рекурсивные шаблоны, которые в smt_rails
очень легко использовать.
Допустим вы хотите отрисовать дерево комментариев, которые сверстаны с помощью вложенных списков примерно так:
<div class="comments">
<ul>
<li>
Comment #1
<ul>
<li>Comment #1.1</li>
<li>Comment #1.2</li>
<li>Comment #1.3</li>
<li>
Comment #1.4
<ul>
<li>Comment #1.4.1</li>
</ul>
</li>
<li>Comment #1.5</li>
</ul>
</li>
<li>Comment #2</li>
<li>Comment #3</li>
</ul>
</div>
Данный кусок HTML можно отрисовать множеством способов, но предположим что вы еще хотите обновлять это дерево с помощью json на клиенте. Посмотрите как легко это можно сделать с помощью mustache-паршиалов:
ruby Gemfile
gem 'smt_rails', '>= 0.2.3'
Отрисовка комментариев на сервере:
<h1>Comments</h1>
<%=
render '/comments', mustache: {
comments: [
{
text: 'Comment #1',
children: [
{text: 'Comment #1.1', children: [], empty: true},
{text: 'Comment #1.2', children: [], empty: true},
{text: 'Comment #1.3', children: [], empty: true},
{
text: 'Comment #1.4',
children: [
{text: 'Comment #1.4.1', children: [], empty: true}
],
empty: false
},
{text: 'Comment #1.5', children: [], empty: true},
],
empty: false
},
{text: 'Comment #2', children: [], empty: true},
{text: 'Comment #3', children: [], empty: true}
]
}
%>
Отрисовка комментариев через javascript (код полностью совпадает, только для отрисовки используется вызов
функции SMT['имя шаблона']()
с параметрами):
// ...
//
// SMT Rails
//= require mustache
//= require_tree ../../templates
$(function(){
$("#place").html(SMT['comments'](
{
comments: [
{
text: 'Comment #1',
children: [
{text: 'Comment #1.1', children: [], empty: true},
{text: 'Comment #1.2', children: [], empty: true},
{text: 'Comment #1.3', children: [], empty: true},
{
text: 'Comment #1.4',
children: [
{text: 'Comment #1.4.1', children: [], empty: true}
],
empty: false
},
{text: 'Comment #1.5', children: [], empty: true},
],
empty: false
},
{text: 'Comment #2', children: [], empty: true},
{text: 'Comment #3', children: [], empty: true}
]
}
));
});
При реальной работе, конечно не нужно будет писать такие жуткие куски куда, подходящий json можно подготовить с помощью простого хелпер-метода:
def comments_to_mustache(comments)
{comments: comments.map { |comment| _comment(comment) }
end
def _comment(comment)
{
text: comment.text,
children: comment.children.map { |child| _comment(child) },
empty: comment.children.empty?
}
end
Отмечу несколько тонких моментов. В серверной реализации mustache (0.99.4)
использование пустого массива children
обязательно, иначе
возникнет бесконечный цикл при отрисовке рекурсивного шаблона. Параметр empty
необходим, чтобы не отрисовывать пустой список
<ul></ul>
если нет вложенных комментариев. Этот параметр - обратная сторона logicless-природы mustache.