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?

Pascal Precht
fuente

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

Yehuda Katz
fuente
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. :)

Vlad GURDIGA
fuente
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) );
});
Jesse Houchins
fuente
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
14

Esto también se puede hacer en versiones posteriores del manillar usando la key=valuenotación:

 {{> mypartial foo='bar' }}

Permitiéndole pasar valores específicos a su contexto parcial.

Referencia: contexto diferente para el parcial # 182

cweston
fuente
1
Esto está disponible a partir de la versión v2.0.0 alpha
Kevin Borders
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>
Andrew C
fuente
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.

Tocino grueso
fuente
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"}]' 
}}
Richard Taylor-Kenny
fuente