Częściowe przekazywanie zmiennych przez kierownicę

133

Obecnie mam do czynienia z handlebars.js w aplikacji express.js. Aby zachować modułowość, dzielę wszystkie moje szablony na części.

Mój problem : nie mogłem znaleźć sposobu na przekazanie zmiennych przez częściowe wywołanie. Powiedzmy, że mam częściową, która wygląda tak:

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

Załóżmy, że zarejestrowałem tę część pod nazwą „myPartial”. W innym szablonie mogę wtedy powiedzieć coś takiego:

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

To działa dobrze, częściowe będą renderowane zgodnie z oczekiwaniami i jestem szczęśliwym programistą. Ale to, czego teraz potrzebuję, to sposób na przekazywanie różnych zmiennych przez to wywołanie, aby na przykład sprawdzić w części, czy podano nagłówek, czy nie. Coś jak:

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

A wywołanie powinno wyglądać mniej więcej tak:

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

lub tak.

Wiem, że jestem w stanie zdefiniować wszystkie potrzebne mi dane, zanim wyrenderuję szablon. Ale potrzebuję sposobu, aby to zrobić, jak właśnie wyjaśniono. Czy jest możliwy sposób?

Pascal Precht
źródło

Odpowiedzi:

216

Części kierownic przyjmują drugi parametr, który staje się kontekstem dla części:

{{> person this}}

W wersjach v2.0.0 alpha i nowszych możesz również przekazać skrót nazwanych parametrów:

{{> person headline='Headline'}}

Możesz zobaczyć testy dla tych scenariuszy: https://github.com/wycats/handlebars.js/blob/ce74c36118ffed1779889d97e6a2a1028ae61510/spec/qunit_spec.js#L456-L462 https://github.com/wycats/handlebars.js/ blob / e290ec24f131f89ddf2c6aeb707a4884d41c3c6d / spec / partss.js # L26-L32

Yehuda Katz
źródło
5
Nie jest od razu jasne, jak to się odnosi do twojego scenariusza? Czy mógłbyś zapisać rozwiązanie - proszę zastosować je w swoim przypadku? Dzięki!
serverman
12
@Yehuda Katz zamiast thismówić, czy mógłbyś podać swój własny kontekst. Na przykład zdefiniuj dodatkowe dane do przekazania, takie jak {new_variable: some_data}?
Tri Nguyen,
22
Chociaż umiejętność przekazywania „tego” jest przyjemna, nie zawsze wystarcza. Często chcesz ponownie wykorzystać pewien fragment kodu HTML potencjalnie na tej samej stronie, ale jesteś skazany na porażkę, jeśli część ma identyfikatory ... ten sam identyfikator pojawi się więcej niż raz i stanie się nieważny. Byłoby niezwykle przydatne, gdybyś mógł przekazać argumenty do podrzędnych podczas wywoływania go, aby jeszcze bardziej dostosować jego zawartość.
Xavier_Ex
2
Która wersja kierownicy to obsługuje? Używam 1.3.0 i ma błąd kompilacji podczas próby przekazania json przez{{> partialName {new_variable: some_data} }}
bafromca
1
@bafromca to dokładnie problem, w którym nie można przekazać dowolnych danych, ale tylko jeden obiekt. Więc albo przekazujesz to, albo tworzysz nową właściwość, która zwraca twoje dane json w kontrolerze lub widoku. Po drugie, powinno być możliwe przekazywanie dowolnych danych do częściowych w postaci plików key=value. Czy jest jakiś problem dotyczący tego w Github?
ohcibi
18

Na wszelki wypadek oto co zrobiłem, aby uzyskać częściowe argumenty. Stworzyłem małego pomocnika, który przyjmuje częściową nazwę i skrót parametrów, które zostaną przekazane do części:

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

Kluczową rzeczą jest to, że pomocnicy Handlebars akceptują hash argumentów podobny do Rubiego . W kodzie pomocniczym pojawiają się jako część ostatniego argumentu funkcji options- - w jej składniku hash. W ten sposób możesz otrzymać pierwszy argument - częściową nazwę - a następnie pobrać dane.

Następnie prawdopodobnie zechcesz zwrócić Handlebars.SafeStringpomocnikowi lub użyć „potrójnego skrytki” - {{{-, aby zapobiec podwójnemu ucieczce.

Oto mniej lub bardziej kompletny scenariusz użycia:

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

Mam nadzieję, że to pomoże… komuś. :)

Vlad GURDIGA
źródło
15

Jest to bardzo możliwe, jeśli napiszesz własnego pomocnika. Używamy niestandardowego $pomocnika do realizacji tego typu interakcji (i nie tylko):

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

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
źródło
1
Aby mieć dostęp do przekazanych argumentów, musisz poszukać ich w obiekcie „hash”: {{hash.foo}}. (Jestem nowy z kierownicą i zajęło mi to trochę czasu) - Dzięki, świetny pomocnik!
Claudio Bredfeldt
Zauważ, że wymaga to prekompilacji podszablonów przed użyciem helpera. Używam Handlebars w node.js i stwierdziłem, że nie zawsze tak było (części składowe były kompilowane na żądanie). Musiałem dodać prostego pomocnika do wstępnej kompilacji częściowych po ich załadowaniu, to działało świetnie!
Dan,
@Czy masz szansę udostępnić tego pomocnika? :)
Tom
1
@Tom, oto jest (nie mogę wymyślić, jak ładnie to sformatować, przepraszam): 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 Prawdopodobnie lepiej dodać to jako własną odpowiedź.
alex
14

Można to również zrobić w późniejszych wersjach kierownic, używając key=valuenotacji:

 {{> mypartial foo='bar' }}

Umożliwienie przekazania określonych wartości do częściowego kontekstu.

Odniesienie: inny kontekst dla części nr 182

cweston
źródło
2
Jest to dostępne począwszy od wersji v2.0.0 alpha
Kevin Borders
9

Zaakceptowana odpowiedź działa świetnie, jeśli chcesz po prostu użyć innego kontekstu w części. Jednak nie pozwala ci odwołać się do żadnego kontekstu nadrzędnego. Aby przekazać wiele argumentów, musisz napisać własnego pomocnika. Oto działający pomocnik dla 2.0.0kierownic (druga odpowiedź działa dla wersji <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) );
});

Następnie w swoim szablonie możesz zrobić coś takiego:

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

Po części będziesz mieć dostęp do tych wartości jako kontekstu, takiego jak:

<div id={{bar.id}}>{{foo}}</div>
Andrew C.
źródło
Wypróbowałem tę wersję z Handlebars 1.0.0 i działała bez zarzutu.
Christopher Lörken
gdzie to „wyszukuje” częściową nazwę „...”?
NickVH
8

Wygląda na to, że chcesz zrobić coś takiego:

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

Yehuda dał ci już sposób na zrobienie tego:

{{> person this}}

Ale żeby wyjaśnić:

Aby nadać częściowi własne dane, po prostu nadaj jej własny model w istniejącym modelu, na przykład:

{{> person this.childContext}}

Innymi słowy, jeśli to jest model, który dajesz swojemu szablonowi:

var model = {
    some : 'attribute'
}

Następnie dodaj nowy obiekt, który ma być podany do części:

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

childContextstaje się kontekstem częściowości, jak powiedział Yehuda - w tym widzi tylko pole another, ale nie widzi (lub nie dba o pole some). Jeśli miałeś idmodel najwyższego poziomu i powtórz to idponownie w childContext, to zadziała dobrze, ponieważ część widzi tylko to, co jest w środku childContext.

Chunky Bacon
źródło
1

Nie jestem pewien, czy jest to pomocne, ale oto przykład szablonu Handlebars z dynamicznymi parametrami przekazywanymi do wbudowanej części RadioButtons i klientem (przeglądarką) renderującym przyciski radiowe w kontenerze.

Do mojego użytku jest renderowany za pomocą Handlebars na serwerze i pozwala klientowi dokończyć proces. Dzięki niemu narzędzie do formularzy może udostępniać dane wbudowane w kierownicy bez pomocy.

Uwaga: ten przykład wymaga 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
źródło