Node.js с Handlebars.js на сервере и клиенте
У меня есть приложение в Node.js с использованием Expressjs и Handlebars в качестве механизма шаблонов.
Expressjs использует макеты, а затем отображает представления. Макет (layout.hbs) выглядит так:
<!doctype html>
<html lang="en">
<head>
</head>
<body>
{{{body}}}
</body>
</html>
{{{body}}}
заменяется на стороне сервера в пределах Node.js при доступе к маршруту. Например:
app.get('/', function(req, res){
res.render('index'})
})
Будет заменен тег {{{body}}}
содержимым index.hbs.
Теперь на стороне клиента я использую Backbone.js и хочу использовать Handlebars для представлений, управляемых через Backbone. Проблема в том, что, поскольку эти страницы уже обрабатываются с помощью Handlebars, когда я пытаюсь использовать Handlebars внутри него (или Handlebars in Handlebars), это не сработает. Ошибок нет, просто просто не заменяет теги данными.
Кто-нибудь сталкивался с этим раньше или имел какое-то представление о работе?
Спасибо!
Ответы
Ответ 1
Yup, это липкая проблема - вроде как проблемы с цитированием в сценариях оболочки, которые становятся гнездом крыс в цитируемых цитатах.
Мое решение - использовать jade (a la haml) в expressjs (на стороне сервера) для вывода шаблонов на основе дескрипторов для клиента. Таким образом, сервер использует один синтаксис (нефрит), а клиент использует другое (рули). Я нахожусь на том же перекрестке, что и вы, поэтому у меня есть тот же вызов.
Конечно, нефрит не является существенным (хотя он готов для выражения). Вы можете выбрать любой механизм шаблонов (не для рулей) для сервера и/или вы могли бы использовать рули на сервере с шаблонами шаблонов без рулей на клиенте --- пока два синтаксиса выбранных вами шаблонных двигателей не сталкиваются. Поскольку я использую emberjs на клиенте, и он использует синтаксис handlebars (по умолчанию), я предпочитаю использовать emberjs + handlebars синтаксис на клиенте. Так что expressjs + jade стал естественным для сервера.
Ответ 2
Вы должны использовать предварительно скомпилированные шаблоны клиентов. Они быстрее выполняются и позволяют использовать один и тот же язык шаблонов на сервере и клиенте.
- Установить ручки глобально
npm install handlebars -g
- Предварительно скопируйте свои шаблоны
handlebars client-template1.handlebars -f templates.js
- Включить templates.js
<script src="templates.js"></script>
- Выполнить шаблон
var html = Handlebars.templates["client-template1"](context);
fooobar.com/questions/180422/...
Ответ 3
Легкий способ сделать это - просто добавить \
до {{
в файле Handlebars. Например:
<script type="text/x-template" id="todo-item-template">
<div class="todo-view">
<input type="checkbox" class="todo-checkbox" \{{checked}}>
<span class="todo-content" tabindex="0">\{{text}}</span>
</div>
<div class="todo-edit">
<input type="text" class="todo-input" value="\{{text}}">
</div>
<a href="#" class="todo-remove" title="Remove this task">
<span class="todo-remove-icon"></span>
</a>
Вышеуказанный код будет отображаться на клиенте с сохраненными тегами {{..}}.
Ответ 4
Бесстыдная самореклама!
Я хотел сделать то же самое, что и клиент/сервер, поэтому я написал небольшой пакет npm, чтобы помочь:
node-handlebars-precompiler
Я взбивал его через пару часов на основе компилятора командной строки в репозитории ручек wycats. Это не самый лучший код в мире, но он очень хорошо работает для меня.
EDIT: я больше не поддерживаю этот пакет. Если вы хотите взять на себя ответственность, свяжитесь со мной через Github. В основном я использую шаблоны Jade, поэтому для меня не имеет смысла продолжать работу в качестве сопровождающего.
Ответ 5
Я работал над этим, передавая клиентские шаблоны через серверные шаблоны.
Таким образом, на стороне сервера прочитайте все ваши клиентские шаблоны в массиве и передайте их функции рендеринга на стороне сервера
В вашем обработчике маршрута сделайте что-то вроде:
readTemplates(function(err, clientTemplates) {
res.render("page", {
clientTemplates: clientTemplates;
});
});
И затем в layout.hbs:
{{#each clientTemplates}}
<script type="text/handlebars id="{{this.filename}}" >
{{{this.template}}}
</script>
{{/each}}
Здесь я использую имена файлов без расширений в качестве идентификатора шаблона, чтобы на них можно ссылаться из представлений Backbone. О, и не забудьте реализовать кэширование для режима производства.
Да, это отстой.
Я думаю, что для этого нужно написать хелпер Handlebars/Express/Connect.
Ответ 6
У вас есть 2 варианта. Второй вариант - лучший способ:
1) Побег усов
<script type="text/x-handlebars" data-hbs="example">
<p>\{{name}}</p>
</script>
2) Прекомпиляция
Это скомпилирует шаблон на сервере, прежде чем он перейдет к клиенту. Это сделает шаблон готовым к использованию и уменьшит нагрузку на браузер.
Ответ 7
Мне не понравилось решение для предварительной компиляции (потому что я хочу определить шаблоны в том же файле, где я их буду использовать), и наивное решение для удаления \{{
(потому что ему нужен полный компилятор Handlebars и более javascript-код), поэтому Я придумал гибридное решение, в котором используются помощники Handlebars:
1) Зарегистрируйте новый помощник под названием "шаблон" в конфигурации сервера
var hbs = require('hbs');
hbs.registerHelper("template", function(key, options){
var source = options.fn().replace("\\{{", "{{");
var ret =
'<script>\n' +
key + ' = function(opt){\n' +
'return Handlebars.template(' + hbs.handlebars.precompile(source) + ')(opt);\n' +
'}\n' +
'</script>';
return ret;
});
2) Используйте его в любом месте вашей клиентской веб-страницы (с \{{
escape для параметров на стороне клиента)
{{#template "myTemplate"}}
<div>
<p>Hello \{{this.name}}!</p>
</div>
{{/template}}
(сервер будет прекомпилировать его примерно так)
<script>
myTemplate = function(opt){
return Handlebars.template(/* HBS PRECOMPILATED FUNCTION */)(opt);
}
</script>
3) Просто вызовите функцию там, где она вам нужна, на javascript на стороне клиента
var generatedHtml = myTemplate("world"); // = <div><p>Hello world!</p></div>
$("#myDiv").html(generatedHtml); // or whatever