Частичная передача переменных через руль

131

В настоящее время я имею дело с handlebars.js в приложении express.js. Чтобы сохранить модульность, я разбил все свои шаблоны на части.

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

<div id=myPartial>
    <h1>Headline<h1>
    <p>Lorem ipsum</p>
</div>

Предположим, я зарегистрировал этот партиал с именем myPartial. В другом шаблоне я могу сказать что-то вроде:

<section>
    {{> myPartial}}
</section>

Это нормально работает, часть будет отрисована, как и ожидалось, и я счастливый разработчик. Но теперь мне нужен способ передачи различных переменных через этот вызов, чтобы, например, в партиале проверить, задан ли заголовок или нет. Что-то вроде:

<div id=myPartial>
    {{#if headline}}
    <h1>{{headline}}</h1>
    {{/if}}
    <p>Lorem Ipsum</p>
</div>

И вызов должен выглядеть примерно так:

<section>
    {{> myPartial|'headline':'Headline'}}
</section>

или так.

Я знаю, что могу определить все данные, которые мне нужны, прежде чем визуализировать шаблон. Но мне нужен способ сделать это, как я только что объяснил. Есть ли способ?

Паскаль Прехт
источник

Ответы:

215

Партиалы Handlebars принимают второй параметр, который становится контекстом для партиала:

{{> person this}}

В версиях v2.0.0 alpha и новее вы также можете передавать хэш именованных параметров:

{{> person headline='Headline'}}

Вы можете увидеть тесты для этих сценариев: https://github.com/wycats/handlebars.js/blob/ce74c36118ffed1779889d97e6a2a1028ae61510/spec/qunit_spec.js#L456-L462 https://github.com/wycats/handlebars/handlebars. BLOB / e290ec24f131f89ddf2c6aeb707a4884d41c3c6d / спецификации / partials.js # L26-L32

Иегуда Кац
источник
5
Не сразу понятно, как это применимо к вашему сценарию? Не могли бы вы записать решение - примените его в вашем случае? Спасибо!
serverman
12
@Yehuda Katz this, не могли бы вы передать свой контекст? Например, определите дополнительные данные для передачи, например {new_variable: some_data}?
Tri Nguyen
22
Хотя возможность передать «это» - это хорошо, этого не всегда достаточно. Часто вы хотите повторно использовать определенную часть html потенциально на той же странице, но вы обречены, если у части есть идентификаторы ... один и тот же идентификатор будет отображаться более одного раза и станет недействительным. Было бы чрезвычайно полезно, если бы вы могли передавать аргументы частичным при его вызове для дальнейшей настройки его содержимого.
Xavier_Ex
2
Какая версия Handlebars поддерживает это? Я использую 1.3.0, и при попытке передать json через{{> partialName {new_variable: some_data} }}
bafromca
1
@bafromca - вот точная проблема: вы не можете передавать произвольные данные, а только один объект. Таким образом, вы либо передаете это, либо создаете новое свойство, которое возвращает ваши json-данные в контроллере или в представлении. Во-вторых, должна быть возможность передавать произвольные данные в частичную в форме key=value. Есть ли какие-нибудь проблемы с этим в github?
ohcibi 01
18

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

Handlebars.registerHelper('render', function(partialId, options) {
  var selector = 'script[type="text/x-handlebars-template"]#' + partialId,
      source = $(selector).html(),
      html = Handlebars.compile(source)(options.hash);

  return new Handlebars.SafeString(html);
});

Ключевым моментом здесь является то, что помощники Handlebars принимают хэш аргументов, подобный Ruby . Во вспомогательном коде они входят как часть последнего аргумента функции options- в ее hashчлене. Таким образом, вы можете получить первый аргумент - частичное имя - и получить данные после него.

Затем вы, вероятно, захотите вернуть a Handlebars.SafeStringот помощника или использовать «тройной тайник» {{{- чтобы предотвратить двойное экранирование.

Вот более-менее полный сценарий использования:

<script id="text-field" type="text/x-handlebars-template">
  <label for="{{id}}">{{label}}</label>
  <input type="text" id="{{id}}"/>
</script>

<script id="checkbox-field" type="text/x-handlebars-template">
  <label for="{{id}}">{{label}}</label>
  <input type="checkbox" id="{{id}}"/>
</script>

<script id="form-template" type="text/x-handlebars-template">
  <form>
    <h1>{{title}}</h1>
    {{ render 'text-field' label="First name" id="author-first-name" }}
    {{ render 'text-field' label="Last name" id="author-last-name" }}
    {{ render 'text-field' label="Email" id="author-email" }}
    {{ render 'checkbox-field' label="Private?" id="private-question" }}
  </form>
</script>

Надеюсь, это поможет… кому-то. :)

Влад ГУРДИГА
источник
15

Это очень возможно, если вы напишете собственный помощник. Мы используем специальный $помощник для выполнения этого (и не только) типа взаимодействия:

/*///////////////////////

Adds support for passing arguments to partials. Arguments are merged with 
the context for rendering only (non destructive). Use `:token` syntax to 
replace parts of the template path. Tokens are replace in order.

USAGE: {{$ 'path.to.partial' context=newContext foo='bar' }}
USAGE: {{$ 'path.:1.:2' replaceOne replaceTwo foo='bar' }}

///////////////////////////////*/

Handlebars.registerHelper('$', function(partial) {
    var values, opts, done, value, context;
    if (!partial) {
        console.error('No partial name given.');
    }
    values = Array.prototype.slice.call(arguments, 1);
    opts = values.pop();
    while (!done) {
        value = values.pop();
        if (value) {
            partial = partial.replace(/:[^\.]+/, value);
        }
        else {
            done = true;
        }
    }
    partial = Handlebars.partials[partial];
    if (!partial) {
        return '';
    }
    context = _.extend({}, opts.context||this, _.omit(opts, 'context', 'fn', 'inverse'));
    return new Handlebars.SafeString( partial(context) );
});
Джесси Хучинс
источник
1
Чтобы получить доступ к переданным аргументам, вам нужно найти их в объекте «хэш»: {{hash.foo}}. (Я новичок в рулях, и мне потребовалось время, чтобы разобраться) - Спасибо, отличный помощник!
Claudio Bredfeldt
Обратите внимание: перед использованием помощника вам необходимо предварительно скомпилировать партиалы. Я использую Handlebars в node.js и обнаружил, что это не всегда так (части были скомпилированы по запросу). Мне пришлось добавить простой помощник для предварительной компиляции частичных файлов после их загрузки, и это отлично сработало!
Дэн
@ Дэн, есть шанс, что ты поделишься с этим помощником? :)
Том
1
@Tom, вот он (не могу понять, как его красиво отформатировать, извините): hbs.registerPartials(path.join(__dirname, '/views/partials'), function() { utils.precompileHandlebarsPartials(hbs); }); // Pre compile the partials precompileHandlebarsPartials : function(hbs) { var partials = hbs.handlebars.partials; for (var partial in partials) { if (typeof partials[partial] === 'string') { partials[partial] = hbs.handlebars.compile(partials[partial]); } }; }
Дэн
@Dan Наверное, лучше добавить это как собственный ответ.
Alex
14

Это также можно сделать в более поздних версиях руля, используя key=valueобозначение:

 {{> mypartial foo='bar' }}

Позволяет передавать определенные значения в частичный контекст.

Ссылка: Другой контекст для части № 182

cweston
источник
1
Это доступно, начиная с версии v2.0.0 alpha
Кевин Бордерс
9

Принятый ответ отлично работает, если вы просто хотите использовать другой контекст в своем частичном. Однако он не позволяет ссылаться на родительский контекст. Чтобы передать несколько аргументов, вам нужно написать собственный помощник. Вот рабочий помощник для Handlebars 2.0.0(другой ответ работает для версий <2.0.0):

Handlebars.registerHelper('renderPartial', function(partialName, options) {
    if (!partialName) {
        console.error('No partial name given.');
        return '';
    }
    var partial = Handlebars.partials[partialName];
    if (!partial) {
        console.error('Couldnt find the compiled partial: ' + partialName);
        return '';
    }
    return new Handlebars.SafeString( partial(options.hash) );
});

Затем в своем шаблоне вы можете сделать что-то вроде:

{{renderPartial 'myPartialName' foo=this bar=../bar}}

И в вашем партиале вы сможете получить доступ к этим значениям как к контексту, например:

<div id={{bar.id}}>{{foo}}</div>
Эндрю С
источник
Я пробовал эту версию с Handlebars 1.0.0, и она работала безупречно.
Кристофер Лёркен,
где этот "поиск" частичного имени "..."?
kemagezien
8

Похоже, вы хотите сделать что-то вроде этого:

{{> person {another: 'attribute'} }}

Иегуда уже дал вам способ сделать это:

{{> person this}}

Но чтобы уточнить:

Чтобы предоставить вашей части собственные данные, просто дайте ей свою собственную модель внутри существующей модели, например:

{{> person this.childContext}}

Другими словами, если это модель, которую вы даете своему шаблону:

var model = {
    some : 'attribute'
}

Затем добавьте новый объект в партиал:

var model = {
    some : 'attribute',
    childContext : {
        'another' : 'attribute' // this goes to the child partial
    }
}

childContextстановится контекстом частичного, как сказал Иегуда - в этом он видит только поле another, но не видит (или не заботится о поле some). Если у вас была idмодель верхнего уровня и повторить еще idраз в childContext, это будет работать нормально, поскольку частичный вид видит только то, что находится внутри childContext.

Толстый бекон
источник
1

Не уверен, что это полезно, но вот пример шаблона Handlebars с динамическими параметрами, переданными во встроенную часть RadioButtons, и клиентом (браузером), отображающим переключатели в контейнере.

Для меня он отображается с помощью Handlebars на сервере и позволяет клиенту завершить его. С его помощью инструмент форм может предоставлять встроенные данные в Handlebars без помощи помощников.

Примечание. Для этого примера требуется jQuery.

{{#*inline "RadioButtons"}}
{{name}} Buttons<hr>
<div id="key-{{{name}}}"></div>
<script>
  {{{buttons}}}.map((o)=>{
    $("#key-{{name}}").append($(''
      +'<button class="checkbox">'
      +'<input name="{{{name}}}" type="radio" value="'+o.value+'" />'+o.text
      +'</button>'
    ));
  });
  // A little test script
  $("#key-{{{name}}} .checkbox").on("click",function(){
      alert($("input",this).val());
  });
</script>
{{/inline}}
{{>RadioButtons name="Radio" buttons='[
 {value:1,text:"One"},
 {value:2,text:"Two"}, 
 {value:3,text:"Three"}]' 
}}
Ричард Тейлор-Кенни
источник