Pasando variables a través del manillar parcial


131

Actualmente estoy tratando con handlebars.js en una aplicación express.js. Para mantener las cosas modulares, dividí todas mis plantillas en parciales.

Mi problema : no pude encontrar una manera de pasar variables a través de una invocación parcial. Digamos que tengo un parcial que se ve así:

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

Supongamos que registré este parcial con el nombre 'myPartial'. En otra plantilla, puedo decir algo como:

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

Esto funciona bien, el parcial se representará como se esperaba y soy un desarrollador feliz. Pero lo que ahora necesito es una forma de pasar diferentes variables a través de esta invocación, para verificar dentro de un parcial, por ejemplo, si se da un título o no. Algo como:

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

Y la invocación debería verse así:

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

más o menos.

Sé que puedo definir todos los datos que necesito antes de representar una plantilla. Pero necesito una forma de hacerlo como acabo de explicar. ¿Hay alguna manera posible?

Respuestas:


214

Los parciales del manillar toman un segundo parámetro que se convierte en el contexto del parcial:

{{> person this}}

En las versiones v2.0.0 alpha y posteriores, también puede pasar un hash de parámetros con nombre:

{{> person headline='Headline'}}

Puede ver las pruebas para estos escenarios: https://github.com/wycats/handlebars.js/blob/ce74c36118ffed1779889d97e6a2a1028ae61510/spec/qunit_spec.js#L456-L462 https://github.com/wycats/handlebars.js/ blob / e290ec24f131f89ddf2c6aeb707a4884d41c3c6d / spec / partials.js # L26-L32


55
¿No está claro de inmediato cómo se aplicaría esto a su escenario? ¿Podría escribir la solución, aplicándola en su caso, por favor? ¡Gracias!
servidor el

12
@Yehuda Katz en lugar de pasar this, podría pasar en su propio contexto. Por ejemplo, defina datos adicionales para transferir, como {new_variable: some_data}?
Tri Nguyen

22
Aunque tener la capacidad de pasar "esto" es bueno, no siempre es suficiente. A menudo, desea reutilizar una determinada pieza de HTML potencialmente en la misma página, pero está condenado si el parcial tiene ID ... la misma ID aparecerá más de una vez y se invalidará. Sería extremadamente útil si puede pasar argumentos a parciales al invocarlo, para personalizar aún más su contenido.
Xavier_Ex

2
¿Qué versión de Handlebars admite esto? Estoy usando 1.3.0 y tiene un error de compilación al intentar pasar json a través de{{> partialName {new_variable: some_data} }}
bafromca

1
@bafromca ese es el problema exacto que no puede pasar datos arbitrarios sino solo un objeto único. Entonces, pasa esto o crea una nueva propiedad que devuelve sus datos json en el controlador o la vista. En segundo lugar, debería ser posible pasar datos arbitrarios a parciales en forma de key=value. ¿Hay algún problema que cubra esto en github?
ohcibi

18

Por si acaso, esto es lo que hice para obtener argumentos parciales, más o menos. He creado un pequeño ayudante que toma un nombre parcial y un hash de parámetros que se pasarán al 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);
});

La clave aquí es que los ayudantes de Handlebars aceptan un hash de argumentos similar a Ruby . En el código auxiliar vienen como parte del último argumento de la función options- en su hashmiembro. De esta manera, puede recibir el primer argumento, el nombre parcial, y obtener los datos después de eso.

Entonces, probablemente desee devolver un mensaje Handlebars.SafeStringdel ayudante o usar "triple-stash" {{{- para evitar que se escape doblemente.

Aquí hay un escenario de uso más o 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 esto ayude a alguien. :)


15

Esto es muy posible si escribe su propio ayudante. Estamos utilizando un $ayudante personalizado para lograr este tipo de interacción (y más):

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

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
Para tener acceso a los argumentos pasados, debe buscarlos en el objeto 'hash': {{hash.foo}}. (Soy nuevo en el manillar y esto me llevó un tiempo descubrir) - ¡Gracias, gran ayudante!
Claudio Bredfeldt

Tenga en cuenta que esto requiere que tenga sus parciales precompilados antes de usar el ayudante. Estoy usando Handlebars en node.js, y descubrí que este no siempre fue el caso (los parciales se compilaron a pedido). Tuve que agregar un ayudante simple para precompilar los parciales después de cargarlos, ¡entonces funcionó muy bien!
Dan

@ ¿Hay alguna posibilidad de que puedas compartir ese ayudante? :)
Tom

1
@ Tom, aquí está (no puedo entender cómo formatearlo bien, lo siento): 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 Probablemente sea mejor agregarlo como su propia respuesta.
alex


9

La respuesta aceptada funciona muy bien si solo desea utilizar un contexto diferente en su parcial. Sin embargo, no le permite hacer referencia a ninguno de los contextos principales. Para pasar varios argumentos, debe escribir su propio ayudante. Aquí hay un asistente de trabajo para Manillares 2.0.0(la otra respuesta funciona para las versiones <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) );
});

Luego, en su plantilla, puede hacer algo como:

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

Y en su parcial, podrá acceder a esos valores como contexto como:

<div id={{bar.id}}>{{foo}}</div>

He probado esta versión con Handlebars 1.0.0 y funcionó a la perfección.
Christopher Lörken

¿Dónde 'busca' un parcial llamado '...'?
kemagezien

8

Parece que quieres hacer algo como esto:

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

Yehuda ya te dio una forma de hacerlo:

{{> person this}}

Pero para aclarar:

Para darle a su parcial sus propios datos, simplemente dele su propio modelo dentro del modelo existente, así:

{{> person this.childContext}}

En otras palabras, si este es el modelo que le está dando a su plantilla:

var model = {
    some : 'attribute'
}

Luego agregue un nuevo objeto para ser dado al parcial:

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

childContextse convierte en el contexto de lo parcial como dijo Yehuda: en eso, solo ve el campo another, pero no ve (o no le importa el campo some). Si tenía iden el modelo de nivel superior, y repite idnuevamente en el childContext, eso funcionará bien ya que el parcial solo ve lo que hay dentro childContext.



1

No estoy seguro de si esto es útil, pero aquí hay un ejemplo de plantilla de Manillar con parámetros dinámicos pasados ​​a un RadioButtons parcial en línea y al cliente (navegador) que muestra los botones de radio en el contenedor.

Para mi uso, se representa con Handlebars en el servidor y permite que el cliente lo termine. Con ella, una herramienta de formularios puede proporcionar datos en línea dentro de Manillares sin ayudantes.

Nota: este ejemplo requiere 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"}]' 
}}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.