Passando variáveis ​​pelo guidão parcial

131

Atualmente, estou lidando com handlebars.js em um aplicativo express.js. Para manter as coisas modulares, divido todos os meus modelos em parciais.

Meu problema : não consegui encontrar uma maneira de passar variáveis ​​através de uma invocação parcial. Digamos que eu tenho um parcial que se parece com isso:

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

Vamos supor que registrei esta parcial com o nome 'myPartial'. Em outro modelo, posso dizer algo como:

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

Isso funciona bem, a parcial será renderizada conforme o esperado e eu sou um desenvolvedor feliz. Mas o que eu preciso agora é de uma maneira de passar variáveis ​​diferentes por essa invocação, para verificar de forma parcial, por exemplo, se um título é fornecido ou não. Algo como:

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

E a invocação deve ser algo como isto:

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

ou então.

Eu sei que sou capaz de definir todos os dados necessários antes de renderizar um modelo. Mas eu preciso de uma maneira de fazer isso, como acabei de explicar. Existe uma maneira possível?

Pascal Precht
fonte

Respostas:

214

Parciais do guidão usam um segundo parâmetro que se torna o contexto da parcial:

{{> person this}}

Nas versões v2.0.0 alfa e posterior, você também pode transmitir um hash de parâmetros nomeados:

{{> person headline='Headline'}}

Você pode ver os testes para estes cenários: https://github.com/wycats/handlebars.js/blob/ce74c36118ffed1779889d97e6a2a1028ae61510/spec/qunit_spec.js#L456-L462 https://github.com/wycats/handlebars.js/ blob / e290ec24f131f89ddf2c6aeb707a4884d41c3c6d / spec / parcials.js # L26-L32

Yehuda Katz
fonte
5
Não está claro imediatamente como isso se aplicaria ao seu cenário? Você poderia escrever a solução - aplicando-a no seu caso, por favor? Obrigado!
serverman
12
@Yehuda Katz, em vez de passar this, você poderia passar no seu próprio contexto. Por exemplo, defina dados extras a serem passados, como {new_variable: some_data}?
Tri Nguyen
22
Embora ter a capacidade de passar "isso" seja bom, nem sempre é suficiente. Muitas vezes, você deseja reutilizar um determinado pedaço de html potencialmente na mesma página, mas está condenado se a parcial tiver IDs ... a mesma ID aparecerá mais de uma vez e se tornará inválida. Seria extremamente útil se você pudesse passar argumentos para parciais ao invocá-lo, para personalizar ainda mais seu conteúdo.
Xavier_Ex
2
Qual versão do guidão suporta isso? Eu estou usando 1.3.0 e tem um erro de compilação ao tentar passar json via{{> partialName {new_variable: some_data} }}
bafromca
1
@bafromca é o problema exato: você não pode transmitir dados arbitrários, mas apenas um único objeto. Então você passa isso ou cria uma nova propriedade que retorna seus dados json no controlador ou na visualização. Em segundo lugar, deveria ser possível passar dados arbitrários para parciais na forma de key=value. Existe algum problema abordando isso no github?
Ohcibi
18

Apenas no caso, aqui está o que eu fiz para obter argumentos parciais, mais ou menos. Eu criei um pequeno ajudante que leva um nome parcial e um hash de parâmetros que serão passados ​​para o parcial:

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);
});

O principal aqui é que os auxiliares do guidão aceitam um hash de argumentos do tipo Ruby . No código auxiliar, eles vêm como parte do último argumento da função options- - em seu hashmembro. Dessa forma, você pode receber o primeiro argumento - o nome parcial - e obter os dados depois disso.

Então, você provavelmente deseja devolver um Handlebars.SafeStringdo ajudante ou usar o "esconderijo triplo" - {{{- para impedir que ele escape duas vezes.

Aqui está um cenário de uso mais ou menos completo:

<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>

Espero que isso ajude ... alguém. :)

Vlad GURDIGA
fonte
15

Isso é muito possível se você escrever seu próprio ajudante. Estamos usando um $auxiliar personalizado para realizar esse tipo de interação (e mais):

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

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) );
});
Jesse Houchins
fonte
1
Para ter acesso aos argumentos passados, você precisa procurá-los no objeto 'hash': {{hash.foo}}. (Eu sou novo no guidão e isso me levou um tempo para descobrir) - Obrigado, ótimo ajudante!
Claudio Bredfeldt
Observe que isso exige que você tenha suas partes pré-compiladas antes de usar o auxiliar. Estou usando o guidão no node.js e descobri que esse nem sempre era o caso (as parciais eram compiladas sob demanda). Eu tive que adicionar um auxiliar simples para pré-compilar parciais depois que elas foram carregadas, então isso funcionou muito bem!
Dan
@ Alguma chance de você compartilhar esse ajudante? :)
Tom
1
@ Tom, aqui é (não consigo descobrir como formatar-lo muito bem, sorry): 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
@ Dan Provavelmente é melhor adicioná-lo como sua própria resposta.
alex
14

Isso também pode ser feito em versões posteriores do guidão usando a key=valuenotação:

 {{> mypartial foo='bar' }}

Permitindo que você passe valores específicos para o seu contexto parcial.

Referência: Contexto diferente para o parcial # 182

cweston
fonte
1
Isto está disponível na versão v2.0.0 alpha
Kevin Borders
9

A resposta aceita funciona muito bem se você deseja apenas usar um contexto diferente na sua parcial. No entanto, não permite que você faça referência a nenhum contexto pai. Para passar vários argumentos, você precisa escrever seu próprio auxiliar. Aqui está um auxiliar de trabalho para o guidão 2.0.0(a outra resposta funciona para versões <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) );
});

Em seguida, no seu modelo, você pode fazer algo como:

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

E no seu parcial, você poderá acessar esses valores como contexto, como:

<div id={{bar.id}}>{{foo}}</div>
Andrew C
fonte
Eu tentei esta versão com o guidão 1.0.0 e funcionou perfeitamente.
Christopher Lörken 13/03/2015
onde essa 'pesquisa' por uma parcial chamada '...'?
kemagezien 6/04
8

Parece que você quer fazer algo assim:

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

Yehuda já lhe deu uma maneira de fazer isso:

{{> person this}}

Mas para esclarecer:

Para fornecer dados parciais a seus próprios dados, basta fornecer seu próprio modelo dentro do modelo existente, da seguinte forma:

{{> person this.childContext}}

Em outras palavras, se este é o modelo que você está dando ao seu modelo:

var model = {
    some : 'attribute'
}

Em seguida, adicione um novo objeto a ser dado à parcial:

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

childContexttorna-se o contexto do parcial como Yehuda disse - nisso, ele apenas vê o campo another, mas não vê (ou se preocupa com o campo some). Se você tivesse idno modelo de nível superior e repita idnovamente no childContext, isso funcionará muito bem, pois a parcial só vê o que está dentro childContext.

Chunky Bacon
fonte
1

Não tenho certeza se isso é útil, mas aqui está um exemplo de modelo de guidão com parâmetros dinâmicos passados ​​para um RadioButtons parcial em linha e o cliente (navegador) processando os botões de opção no contêiner.

Para meu uso, ele é renderizado com guidão no servidor e permite que o cliente finalize. Com ele, uma ferramenta de formulários pode fornecer dados embutidos no Guiador sem auxiliares.

Nota: Este exemplo requer 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"}]' 
}}
Richard Taylor-Kenny
fonte