Passage de variables à travers le guidon partiel

131

Je travaille actuellement avec handlebars.js dans une application express.js. Pour garder les choses modulaires, j'ai divisé tous mes modèles en partiels.

Mon problème : je n'ai pas trouvé de moyen de passer des variables via une invocation partielle. Disons que j'ai un partiel qui ressemble à ceci:

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

Supposons que j'ai enregistré ce partiel avec le nom «myPartial». Dans un autre modèle, je peux alors dire quelque chose comme:

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

Cela fonctionne bien, le partiel sera rendu comme prévu et je suis un développeur heureux. Mais ce dont j'ai besoin maintenant, c'est d'un moyen de passer différentes variables à travers cette invocation, pour vérifier dans un partiel par exemple, si un titre est donné ou non. Quelque chose comme:

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

Et l'invocation devrait ressembler à ceci:

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

ou alors.

Je sais que je suis capable de définir toutes les données dont j'ai besoin, avant de rendre un modèle. Mais j'ai besoin d'un moyen de le faire comme je viens de l'expliquer. Y a-t-il un moyen possible?

Pascal Precht
la source

Réponses:

214

Les partiels de guidon prennent un deuxième paramètre qui devient le contexte du partiel:

{{> person this}}

Dans les versions v2.0.0 alpha et supérieures, vous pouvez également passer un hachage de paramètres nommés:

{{> person headline='Headline'}}

Vous pouvez voir les tests pour ces scénarios: https://github.com/wycats/handlebars.js/blob/ce74c36118ffed1779889d97e6a2a1028ae61510/spec/qunit_spec.js#L456-L462 https://github.com/wycats/shandlebars. blob / e290ec24f131f89ddf2c6aeb707a4884d41c3c6d / spec / partials.js # L26-L32

Yehuda Katz
la source
5
On ne sait pas immédiatement comment cela s'appliquerait à votre scénario? Pourriez-vous écrire la solution - l'appliquer dans votre cas, s'il vous plaît? Merci!
serverman
12
@Yehuda Katz au lieu de passer this, pourriez-vous passer dans votre propre contexte. Par exemple, définissez des données supplémentaires à transmettre, telles que {new_variable: some_data}?
Tri Nguyen
22
Bien qu'avoir la capacité de passer "ceci" soit agréable, ce n'est pas toujours suffisant. Souvent, vous souhaitez réutiliser un certain morceau de html potentiellement sur la même page, mais vous êtes condamné si le partiel a des ID ... le même ID apparaîtra plus d'une fois et deviendra invalide. Ce serait extrêmement utile si vous pouvez passer des arguments aux partiels lors de son appel, pour personnaliser davantage son contenu.
Xavier_Ex
2
Quelle version de Handlebars prend en charge cela? J'utilise 1.3.0 et il y a une erreur de compilation en essayant de passer json via{{> partialName {new_variable: some_data} }}
bafromca
1
@bafromca c'est le problème exact que vous ne pouvez pas transmettre de données arbitraires mais un seul objet. Donc, soit vous passez ceci, soit vous créez une nouvelle propriété qui renvoie vos données json dans le contrôleur ou la vue. Je seconde qu'il devrait être possible de passer des données arbitraires à partielles sous la forme de key=value. Y a-t-il un problème pour couvrir cela dans github?
ohcibi
18

Juste au cas où, voici ce que j'ai fait pour obtenir des arguments partiels, en quelque sorte. J'ai créé un petit assistant qui prend un nom partiel et un hachage de paramètres qui seront passés au partiel:

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

L'essentiel ici est que les assistants Handlebars acceptent un hachage d'arguments de type Ruby . Dans le code d'assistance, ils font partie du dernier argument de la fonction options- - dans son hashmembre. De cette façon, vous pouvez recevoir le premier argument - le nom partiel - et obtenir les données après cela.

Ensuite, vous voudrez probablement renvoyer a Handlebars.SafeStringde l'assistant ou utiliser «triple-stash» - {{{- pour l'empêcher de double échapper.

Voici un scénario d'utilisation plus ou moins complet:

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

J'espère que cela aide… quelqu'un. :)

Vlad GURDIGA
la source
15

C'est très possible si vous écrivez votre propre aide. Nous utilisons une $aide personnalisée pour accomplir ce type d'interaction (et plus):

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

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
la source
1
Pour avoir accès aux arguments passés, vous devez les rechercher dans l'objet 'hash': {{hash.foo}}. (Je suis nouveau avec le guidon et cela m'a pris un certain temps à comprendre) - Merci, super aide!
Claudio Bredfeldt
Notez que cela nécessite que vous ayez vos partiels pré-compilés avant d'utiliser l'assistant. J'utilise Handlebars dans node.js et j'ai constaté que ce n'était pas toujours le cas (les partiels ont été compilés à la demande). J'ai dû ajouter une aide simple pour pré-compiler les partiels après leur chargement, puis cela a très bien fonctionné!
Dan
@Dan une chance que vous puissiez partager cette aide? :)
Tom
1
@Tom, le voici (je ne sais pas comment le formater correctement, désolé): 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 Il vaut probablement mieux l'ajouter comme sa propre réponse.
alex
14

Cela peut également être fait dans les versions ultérieures du guidon en utilisant la key=valuenotation:

 {{> mypartial foo='bar' }}

Vous permettant de transmettre des valeurs spécifiques à votre contexte partiel.

Référence: Contexte différent pour le partiel # 182

cweston
la source
1
Ceci est disponible à partir de la version v2.0.0 alpha
Kevin Borders
9

La réponse acceptée fonctionne très bien si vous souhaitez simplement utiliser un contexte différent dans votre partiel. Cependant, il ne vous permet de référencer aucun des contextes parent. Pour transmettre plusieurs arguments, vous devez écrire votre propre assistant. Voici une aide de travail pour Handlebars 2.0.0(l'autre réponse fonctionne pour les versions <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) );
});

Ensuite, dans votre modèle, vous pouvez faire quelque chose comme:

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

Et dans votre partiel, vous pourrez accéder à ces valeurs sous forme de contexte comme:

<div id={{bar.id}}>{{foo}}</div>
Andrew C
la source
J'ai essayé cette version avec Handlebars 1.0.0 et cela a fonctionné parfaitement.
Christopher Lörken
où cette «recherche» pour un partiel nommé «...»?
kemagezien le
8

On dirait que vous voulez faire quelque chose comme ceci:

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

Yehuda vous a déjà donné un moyen de le faire:

{{> person this}}

Mais pour clarifier:

Pour donner à votre partiel ses propres données, donnez-lui simplement son propre modèle à l'intérieur du modèle existant, comme ceci:

{{> person this.childContext}}

En d'autres termes, s'il s'agit du modèle que vous donnez à votre modèle:

var model = {
    some : 'attribute'
}

Ajoutez ensuite un nouvel objet à donner au partiel:

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

childContextdevient le contexte du partiel comme Yehuda l'a dit - en cela, il ne voit que le champ another, mais il ne voit pas (ou ne se soucie pas du champ some). Si vous aviez iddans le modèle de niveau supérieur, et répétez à idnouveau dans childContext, cela fonctionnera très bien car le partiel ne voit que ce qu'il y a à l'intérieur childContext.

Chunky Bacon
la source
1

Je ne sais pas si cela est utile, mais voici un exemple de modèle Handlebars avec des paramètres dynamiques passés à un partiel RadioButtons en ligne et le client (navigateur) rendant les boutons radio dans le conteneur.

Pour mon utilisation, il est rendu avec Handlebars sur le serveur et permet au client de le terminer. Avec lui, un outil de formulaires peut fournir des données en ligne dans Handlebars sans aide.

Remarque: cet exemple nécessite 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
la source