Operator logiczny w pliku handlebars.js {{#if}} warunkowy


479

Czy w kierownicy JS jest sposób na włączenie operatorów logicznych do standardowego operatora warunkowego handlebars.js? Coś takiego:

{{#if section1 || section2}}
.. content
{{/if}}

Wiem, że mógłbym napisać własnego pomocnika, ale najpierw chciałbym się upewnić, że nie wymyślę koła na nowo.

Odpowiedzi:


518

Jest to możliwe poprzez „oszustwo” z pomocnikiem bloku. Jest to prawdopodobnie sprzeczne z ideologią ludzi, którzy opracowali Kierownice.

Handlebars.registerHelper('ifCond', function(v1, v2, options) {
  if(v1 === v2) {
    return options.fn(this);
  }
  return options.inverse(this);
});

Następnie możesz zadzwonić do pomocnika w szablonie w ten sposób

{{#ifCond v1 v2}}
    {{v1}} is equal to {{v2}}
{{else}}
    {{v1}} is not equal to {{v2}}
{{/ifCond}}

54
Jest to sprzeczne z logiczną naturą Kierownic / Wąsów, ale z pewnością jest przydatne, dzięki!
Bala Clark,

6
Zauważ, że to po prostu nie działa z powiązanymi właściwościami (czytaj, powiązania Ember). Jest w porządku z literalnymi wartościami, ale nie rozwiązuje właściwości modelu (testowane z Ember 1.0.0-rc.8 i Handlebars 1.0.0) i registerBoundHelpernie radzi sobie ze składnią Handlebars. Obejściem tego problemu jest utworzenie niestandardowego widoku: stackoverflow.com/questions/18005111/…
Warren Seine

28
@BalaClark czy to naprawdę logiczne? Mam na myśli, że nadal ma stwierdzenia „jeśli” - tylko dlatego, że zostały celowo okaleczone, nie zmienia filozofii.
phreakhead

111
Nigdy nie zrozumiałem, dlaczego programiści lubią się upośledzać.
StackOverflow

36
Nie rozumiem, dlaczego AND / OR nie może być częścią Kierownicy. Nie jest to sprzeczne z bezrozumną naturą, ponieważ jest już JEŻELI, JEŚLI I KAŻDY ... tyle dla bezrozumnych
Asaf

445

Posuwając rozwiązanie o krok dalej. To dodaje operator porównania.

Handlebars.registerHelper('ifCond', function (v1, operator, v2, options) {

    switch (operator) {
        case '==':
            return (v1 == v2) ? options.fn(this) : options.inverse(this);
        case '===':
            return (v1 === v2) ? options.fn(this) : options.inverse(this);
        case '!=':
            return (v1 != v2) ? options.fn(this) : options.inverse(this);
        case '!==':
            return (v1 !== v2) ? options.fn(this) : options.inverse(this);
        case '<':
            return (v1 < v2) ? options.fn(this) : options.inverse(this);
        case '<=':
            return (v1 <= v2) ? options.fn(this) : options.inverse(this);
        case '>':
            return (v1 > v2) ? options.fn(this) : options.inverse(this);
        case '>=':
            return (v1 >= v2) ? options.fn(this) : options.inverse(this);
        case '&&':
            return (v1 && v2) ? options.fn(this) : options.inverse(this);
        case '||':
            return (v1 || v2) ? options.fn(this) : options.inverse(this);
        default:
            return options.inverse(this);
    }
});

Użyj go w takim szablonie:

{{#ifCond var1 '==' var2}}

Wersja skryptu kawy

Handlebars.registerHelper 'ifCond', (v1, operator, v2, options) ->
    switch operator
        when '==', '===', 'is'
            return if v1 is v2 then options.fn this else options.inverse this
        when '!=', '!=='
            return if v1 != v2 then options.fn this else options.inverse this
        when '<'
            return if v1 < v2 then options.fn this else options.inverse this
        when '<='
            return if v1 <= v2 then options.fn this else options.inverse this
        when '>'
            return if v1 > v2 then options.fn this else options.inverse this
        when '>='
            return if v1 >= v2 then options.fn this else options.inverse this
        when '&&', 'and'
            return if v1 and v2 then options.fn this else options.inverse this
        when '||', 'or'
            return if v1 or v2 then options.fn this else options.inverse this
        else
            return options.inverse this

20
nie zapomnij o '||'i '&&'. Dodałem te skrzynki i są bardzo przydatne.
Jason

20
Bycie noobem dla kierownicy jedną rzeczą, która nie była jasna, jest to, że musisz przekazać operatora jako ciąg, inaczej kompilator popełni błąd podczas tokenizacji twojego szablonu. {{#ifCond true '==' false}}
Joe Holloway

2
Znalazłem wartości, których nie należy oceniać dla atrybutów obiektu. Dodanie następujących pomaga v1 = Ember.Handlebars.get(this, v1, options) v2 = Ember.Handlebars.get(this, v2, options)
ZX12R

4
ale jeśli v1 i v2 również jeden warunek, to jak mogę użyć? na przykład: if (wartość == "a" || wartość == "b")
Kryszna

3
Czy możesz zrobić z tym coś innego?
jbyrd,

160

Kierownica obsługuje operacje zagnieżdżone. Zapewnia to dużą elastyczność (i czystszy kod), jeśli piszemy naszą logikę nieco inaczej.

{{#if (or section1 section2)}}
.. content
{{/if}}

W rzeczywistości możemy dodać różnego rodzaju logikę:

{{#if (or 
        (eq section1 "foo")
        (ne section2 "bar"))}}
.. content
{{/if}}

Po prostu zarejestruj tych pomocników:

Handlebars.registerHelper({
    eq: (v1, v2) => v1 === v2,
    ne: (v1, v2) => v1 !== v2,
    lt: (v1, v2) => v1 < v2,
    gt: (v1, v2) => v1 > v2,
    lte: (v1, v2) => v1 <= v2,
    gte: (v1, v2) => v1 >= v2,
    and() {
        return Array.prototype.every.call(arguments, Boolean);
    },
    or() {
        return Array.prototype.slice.call(arguments, 0, -1).some(Boolean);
    }
});

5
Zauważ, że nie było to możliwe, dopóki HTMLBars w Ember 1.10. Ponadto ci pomocnicy są dodatkami Ember CLI, jeśli wolisz: github.com/jmurphyau/ember-truth-helpers .
stephen.hanson

1
W ten sposób bez optionswykonywania lepiej jest trzymać się ifmetody i łatwiej testować. Dzięki!
Washington Botelho,

25
Wygląda na to, że wbudowaliśmy Lisp w Kierownicę.
junning

8
Twój andi orpomocnicy pracują tylko z 2 parametrami, co nie jest tym, czego chcesz. Udało mi się przerobić zarówno pomocników, jak andi orpomocników, aby obsługiwały więcej niż dwa parametry. Użyj tej jednej wkładki dla and: return Array.prototype.slice.call(arguments, 0, arguments.length - 1).every(Boolean);i używać tego jednego-liner dla or: return Array.prototype.slice.call(arguments, 0, arguments.length - 1).some(Boolean);.
Łukasz

3
Trochę bardziej zaawansowana wersja , która może trwać wiele parametrów na każdego operatora.
Nate

87

wznosząc to na wyższy poziom, dla tych z was, którzy żyją na krawędzi.

gist : https://gist.github.com/akhoury/9118682 Demo : Fragment kodu poniżej

Pomocnik kierownicy: {{#xif EXPRESSION}} {{else}} {{/xif}}

pomocnik do wykonania instrukcji IF z dowolnym wyrażeniem

  1. EKSPRESJA to poprawnie ocalony ciąg
  2. Tak, POTRZEBUJESZ odpowiednio uciec od literałów ciągowych lub po prostu naprzemiennie pojedynczych i podwójnych cudzysłowów
  3. możesz uzyskać dostęp do dowolnej funkcji globalnej lub właściwości tj encodeURIComponent(property)
  4. ten przykład zakłada, że ​​przekazałeś ten kontekst swoim kierownicom template( {name: 'Sam', age: '20' } ), zauważ, że ageto stringtylko dla, żebym mógł parseInt()później w tym poście zaprezentować

Stosowanie:

<p>
 {{#xif " name == 'Sam' && age === '12' " }}
   BOOM
 {{else}}
   BAMM
 {{/xif}}
</p>

Wynik

<p>
  BOOM
</p>

JavaScript: (zależy od innego pomocnika - czytaj dalej)

 Handlebars.registerHelper("xif", function (expression, options) {
    return Handlebars.helpers["x"].apply(this, [expression, options]) ? options.fn(this) : options.inverse(this);
  });

Pomocnik kierownicy: {{x EXPRESSION}}

Pomocnik do wykonywania wyrażeń javascript

  1. EKSPRESJA to poprawnie ocalony ciąg
  2. Tak, POTRZEBUJESZ odpowiednio uciec od literałów ciągowych lub po prostu naprzemiennie pojedynczych i podwójnych cudzysłowów
  3. możesz uzyskać dostęp do dowolnej funkcji globalnej lub właściwości tj parseInt(property)
  4. ten przykład zakłada, że ​​przekazałeś ten kontekst swoim kierownicom template( {name: 'Sam', age: '20' } ), agejest to stringdla celów demonstracyjnych, może być cokolwiek ...

Stosowanie:

<p>Url: {{x "'hi' + name + ', ' + window.location.href + ' <---- this is your href,' + ' your Age is:' + parseInt(this.age, 10)"}}</p>

Wynik:

<p>Url: hi Sam, http://example.com <---- this is your href, your Age is: 20</p>

JavaScript:

Wygląda to na trochę duże, ponieważ rozszerzyłem składnię i skomentowałem prawie każdą linię dla celów przejrzystości

Handlebars.registerHelper("x", function(expression, options) {
  var result;

  // you can change the context, or merge it with options.data, options.hash
  var context = this;

  // yup, i use 'with' here to expose the context's properties as block variables
  // you don't need to do {{x 'this.age + 2'}}
  // but you can also do {{x 'age + 2'}}
  // HOWEVER including an UNINITIALIZED var in a expression will return undefined as the result.
  with(context) {
    result = (function() {
      try {
        return eval(expression);
      } catch (e) {
        console.warn('•Expression: {{x \'' + expression + '\'}}\n•JS-Error: ', e, '\n•Context: ', context);
      }
    }).call(context); // to make eval's lexical this=context
  }
  return result;
});

Handlebars.registerHelper("xif", function(expression, options) {
  return Handlebars.helpers["x"].apply(this, [expression, options]) ? options.fn(this) : options.inverse(this);
});

var data = [{
  firstName: 'Joan',
  age: '21',
  email: 'joan@aaa.bbb'
}, {
  firstName: 'Sam',
  age: '18',
  email: 'sam@aaa.bbb'
}, {
  firstName: 'Perter',
  lastName: 'Smith',
  age: '25',
  email: 'joseph@aaa.bbb'
}];

var source = $("#template").html();
var template = Handlebars.compile(source);
$("#main").html(template(data));
h1 {
  font-size: large;
}
.content {
  padding: 10px;
}
.person {
  padding: 5px;
  margin: 5px;
  border: 1px solid grey;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.0.0/handlebars.min.js"></script>

<script id="template" type="text/x-handlebars-template">
  <div class="content">
    {{#each this}}
    <div class="person">
      <h1>{{x  "'Hi ' + firstName"}}, {{x 'lastName'}}</h1>
      <div>{{x '"you were born in " + ((new Date()).getFullYear() - parseInt(this.age, 10)) '}}</div>
      {{#xif 'parseInt(age) >= 21'}} login here:
      <a href="http://foo.bar?email={{x 'encodeURIComponent(email)'}}">
        	http://foo.bar?email={{x 'encodeURIComponent(email)'}}
        </a>
      {{else}} Please go back when you grow up. {{/xif}}
    </div>
    {{/each}}
  </div>
</script>

<div id="main"></div>

Moar

jeśli chcesz uzyskać dostęp do zakresu wyższego poziomu, ten jest nieco inny, wyrażenie to JOIN wszystkich argumentów, użycie: powiedz, że dane kontekstowe wyglądają tak:

// data
{name: 'Sam', age: '20', address: { city: 'yomomaz' } }

// in template
// notice how the expression wrap all the string with quotes, and even the variables
// as they will become strings by the time they hit the helper
// play with it, you will immediately see the errored expressions and figure it out

{{#with address}}
    {{z '"hi " + "' ../this.name '" + " you live with " + "' city '"' }}
{{/with}}

JavaScript:

Handlebars.registerHelper("z", function () {
    var options = arguments[arguments.length - 1]
    delete arguments[arguments.length - 1];
    return Handlebars.helpers["x"].apply(this, [Array.prototype.slice.call(arguments, 0).join(''), options]);
});

Handlebars.registerHelper("zif", function () {
    var options = arguments[arguments.length - 1]
    delete arguments[arguments.length - 1];
    return Handlebars.helpers["x"].apply(this, [Array.prototype.slice.call(arguments, 0).join(''), options]) ? options.fn(this) : options.inverse(this);
});

15
To jest świetne. Kierownica nie może ci powiedzieć, co masz robić :)
lemiant

1
:) dzięki, zaktualizowałem GIST trochę dodać kilka bajerów więcej (nie bezpośrednio związane z tym SO pytanie - ale w duchu robić to, co chcesz wewnątrz szablonu kierownicy) Należy jednak zwrócić uwagę na ograniczenia, I haven nie znalazłem sposobu na uzyskanie dostępu do „poziomów górnego zakresu” z poziomu wyrażenia, powiedzmy, że jesteś w każdym zakresie, {{#each}} ... {{xif ' ../name === this.name' }} {{/each}}… ale wciąż badam…
bentael

1
znalazł rozwiązanie dla „dostępu górnego zakresu”, ale używając nowego pomocnika, zobacz zaktualizowaną odpowiedź, {{z ...}}pomocnik
bentael

BUM ! Wspaniale, teraz mogę częściowo spełnić moje warunki !
AncAinu

1
@ rickysullivan spróbuj zastąpić eval(expression);to: eval('(function(){try{return ' + expression + ';}catch(e){}})();');- Wiem, że robi się to brzydkie, ale nie mogę wymyślić bezpiecznego sposobu na zrobienie tego - pamiętaj, że wykonanie tego nie jest zbyt „szybkie”, nie zrobiłbym tego w wielka iteracja.
bentael

33

Jest prosty sposób na zrobienie tego bez pisania funkcji pomocnika ... Można to zrobić całkowicie w szablonie.

{{#if cond1}}   
  {{#if con2}}   
    <div> and condition completed</div>  
  {{/if}}
{{else}}   
  <div> both conditions weren't true</div>  
{{/if}}

Edycja: I odwrotnie, możesz to zrobić:

{{#if cond1}}  
  <div> or condition completed</div>    
{{else}}   
  {{#if cond2}}  
    <div> or condition completed</div>  
  {{else}}      
    <div> neither of the conditions were true</div>    
  {{/if}}  
{{/if}}

Edycja / Uwaga: Na stronie kierownicy: handlebarsjs.com oto wartości fałszu:

Możesz użyć pomocnika if, aby warunkowo renderować blok. Jeśli jego argument zwróci false, niezdefiniowany, null, „” lub [] (wartość „falsy”), wówczas wszelkie „cond” (jak cond1 lub cond2) nie będą liczone jako prawdziwe.


9
Nie bardzo, nie porównujesz dwóch wartości, po prostu upewniasz się, że obie istnieją, jest inna.
Cyril N.

3
W rzeczywistości ocenia to w ten sam sposób, w jaki robi javascript, ze strony internetowej: „Możesz użyć pomocnika if, aby warunkowo renderować blok. Jeśli jego argument zwraca wartość false, niezdefiniowany, null” lub „[] (wartość„ falsy ”), Kierownice nie renderują bloku ”.
Jono

1
Masz rację, po pierwsze pomyślałem, że test polega na porównaniu dwóch wartości, a nie na obu testach. Przykro mi, przepraszam.
Cyril N.

4
To nie działa poprawnie. Jeśli cond1 jest prawdą, a con2 jest fałszem, nic nie zostanie wydrukowane.
Tessa Lau,

1
Hej @TessaLau, myślę, że widzę, skąd pochodzisz. Jeśli jednak narysujesz przepływ kontrolny, zobaczysz, że w pierwszej linii przesuwa przepływ kontrolny do tego stanu.
Jono

19

Jednym problemem z wszystkimi zamieszczonymi tutaj odpowiedziami jest to, że nie działają one z powiązanymi właściwościami, tj. Warunek if nie jest ponownie oceniany, gdy zmieniają się związane z nim właściwości. Oto nieco bardziej zaawansowana wersja pomocnika obsługującego wiązania. Używa funkcji wiązania ze źródła Embera, która jest również używana do implementacji normalnego #ifpomocnika Embera .

Ta jest ograniczona do pojedynczej wiązanej właściwości po lewej stronie, w porównaniu do stałej po prawej stronie, która moim zdaniem jest wystarczająca do większości praktycznych celów. Jeśli potrzebujesz czegoś bardziej zaawansowanego niż proste porównanie, być może dobrze byłoby zacząć deklarować niektóre obliczone właściwości i #ifzamiast tego używać normalnego pomocnika.

Ember.Handlebars.registerHelper('ifeq', function(a, b, options) {
  return Ember.Handlebars.bind.call(options.contexts[0], a, options, true, function(result) {
    return result === b;
  });
});

Możesz użyć tego w następujący sposób:

{{#ifeq obj.some.property "something"}}
  They are equal!
{{/ifeq}}

To jest odpowiednia odpowiedź, jeśli używasz Embera. Inne rozwiązania, o których wspomniałeś, po prostu przekażą klucz zamiast wartości. BTW dzięki za to, spędziłem kilka godzin marszcząc głowę.
J Lee

2
Czy ktoś dostał tę pracę z HTMLBars / Ember 1.10? Ember.Handlebars.bind wydaje się już nie istnieć.
Linda,

Pierwotnie użyłem registerBoundHelper początkowo, ale potem, gdy zmienił warunek, nie zmieniłby się na wartość else i wrócił .. Ta metoda działa z ember do zmiany :) Powinien mieć o wiele więcej głosów
Matt Vukomanovic

13

Ulepszone rozwiązanie, które w zasadzie działa z dowolnym operatorem binarnym (przynajmniej liczby, ciągi nie działają dobrze z eval, UWAŻAJ NA MOŻLIWE WSTRZYKIWANIE SKRYPTÓW JEŻELI UŻYWAĆ NIEZDEFINIOWANEGO OPERATORA Z WEJŚCIAMI UŻYTKOWNIKA):

Handlebars.registerHelper("ifCond",function(v1,operator,v2,options) {
    switch (operator)
    {
        case "==":
            return (v1==v2)?options.fn(this):options.inverse(this);

        case "!=":
            return (v1!=v2)?options.fn(this):options.inverse(this);

        case "===":
            return (v1===v2)?options.fn(this):options.inverse(this);

        case "!==":
            return (v1!==v2)?options.fn(this):options.inverse(this);

        case "&&":
            return (v1&&v2)?options.fn(this):options.inverse(this);

        case "||":
            return (v1||v2)?options.fn(this):options.inverse(this);

        case "<":
            return (v1<v2)?options.fn(this):options.inverse(this);

        case "<=":
            return (v1<=v2)?options.fn(this):options.inverse(this);

        case ">":
            return (v1>v2)?options.fn(this):options.inverse(this);

        case ">=":
         return (v1>=v2)?options.fn(this):options.inverse(this);

        default:
            return eval(""+v1+operator+v2)?options.fn(this):options.inverse(this);
    }
});

jak użyłbyś tego w szablonie? zwłaszcza część options.fn?
qodeninja

{{ifCond val1 ”||” val2}} true {{else}} false {{/ if}} zwraca options.fn (true, klauzula ifCond), jeśli jest poprawna, w przeciwnym razie zwraca options.inverse (false, klauzula else), jeśli jest niepoprawna.
Nick Kitto

1
Ze względu na wspomniane zastrzeżenie dotyczące wstrzykiwania skryptu zdecydowanie odradzam korzystanie z tego pomocnika. W dużej bazie kodu może to być z łatwością odpowiedzialne za paskudny problem bezpieczeństwa na całej linii
Jordan Sitkin

8

Oto rozwiązanie, jeśli chcesz sprawdzić wiele warunków:

/* Handler to check multiple conditions
   */
  Handlebars.registerHelper('checkIf', function (v1,o1,v2,mainOperator,v3,o2,v4,options) {
      var operators = {
           '==': function(a, b){ return a==b},
           '===': function(a, b){ return a===b},
           '!=': function(a, b){ return a!=b},
           '!==': function(a, b){ return a!==b},
           '<': function(a, b){ return a<b},
           '<=': function(a, b){ return a<=b},
           '>': function(a, b){ return a>b},
           '>=': function(a, b){ return a>=b},
           '&&': function(a, b){ return a&&b},
           '||': function(a, b){ return a||b},
        }
      var a1 = operators[o1](v1,v2);
      var a2 = operators[o2](v3,v4);
      var isTrue = operators[mainOperator](a1, a2);
      return isTrue ? options.fn(this) : options.inverse(this);
  });

Stosowanie:

/* if(list.length>0 && public){}*/

{{#checkIf list.length '>' 0 '&&' public '==' true}} <p>condition satisfied</p>{{/checkIf}}

7

Oto link do pomocnika bloku, którego używam: pomocnika bloku porównania . Obsługuje wszystkie standardowe operatory i pozwala pisać kod, jak pokazano poniżej. To naprawdę bardzo przydatne.

{{#compare Database.Tables.Count ">" 5}}
There are more than 5 tables
{{/compare}}

1
To powinien być zwycięski głos. Natywny i zamierzony. Uważam, że prawie za każdym razem, gdy piszesz nowego pomocnika, zastanawiasz się nad tym.
sierpnia

2
@ augurone to nie jest rodzimy pomocnik. Jeśli podążysz za linkiem, zobaczysz, że jest to niestandardowy pomocnik.
gfullam

@gfullam masz rację, korzystałem z kierownicy podczas montażu, która obejmuje natywnie tego pomocnika. Mój błąd.
sierpnia

4

Podobnie do odpowiedzi Jima, ale przy odrobinie kreatywności możemy również zrobić coś takiego:

Handlebars.registerHelper( "compare", function( v1, op, v2, options ) {

  var c = {
    "eq": function( v1, v2 ) {
      return v1 == v2;
    },
    "neq": function( v1, v2 ) {
      return v1 != v2;
    },
    ...
  }

  if( Object.prototype.hasOwnProperty.call( c, op ) ) {
    return c[ op ].call( this, v1, v2 ) ? options.fn( this ) : options.inverse( this );
  }
  return options.inverse( this );
} );

Następnie, aby go użyć, otrzymujemy coś takiego:

{{#compare numberone "eq" numbretwo}}
  do something
{{else}}
  do something else
{{/compare}}

Sugerowałbym przeniesienie obiektu poza funkcję dla lepszej wydajności, ale w przeciwnym razie możesz dodać dowolną funkcję porównywania, w tym „i” oraz „lub”.


3

Inną alternatywą jest użycie nazwy funkcji w #if. #ifWykryje jeśli parametr jest funkcja i jeśli jest to będzie nazwać i używać jej powrót do odprawy truthyness. Poniżej myFunction pobiera aktualny kontekst jako this.

{{#if myFunction}}
  I'm Happy!
{{/if}}

Więc musiałbyś dodać funkcję do kontekstu? Wykonywanie kodu z kontekstu stanowi lukę w zabezpieczeniach, ponieważ źródło kodu może być nieznane. Można to wykorzystać do ataku XSS.
T Nguyen,

Tak, musisz dodać funkcję do kontekstu. Tak, na źle zaprojektowanej stronie internetowej może to być dziura w zabezpieczeniach. Ale w takim przypadku byłoby wiele innych.
Shital Shah,

Jest to preferowany sposób .. TY.
elad.chen

3

Niestety żadne z tych rozwiązań nie rozwiązuje problemu operatora „OR” cond1 || cond2 ”.

  1. Sprawdź, czy pierwsza wartość jest prawdą
  2. Użyj „^” (lub) i sprawdź, czy inaczej cond2 jest prawdziwe

    {{#if cond1}} ZRÓB DZIAŁANIE {{^}} {{#if cond2}} ZRÓB DZIAŁANIE {{/ if}} {{/ if}}

Łamie zasadę SUCHEGO. Dlaczego więc nie użyć częściowego, aby zmniejszyć bałagan?

{{#if cond1}}
    {{> subTemplate}}
{{^}}
    {{#if cond2}}
        {{> subTemplate}}
    {{/if}}
{{/if}}

3

Rozumiem, dlaczego chcesz stworzyć pomocnika w sytuacjach, w których masz wiele różnych porównań do wykonania w szablonie, ale dla stosunkowo niewielkiej liczby porównań (lub nawet jednego, co doprowadziło mnie do tej strony w pierwsze miejsce), prawdopodobnie łatwiej byłoby zdefiniować nową zmienną kierownicy w wywołaniu funkcji renderowania widoku, na przykład:

Przejdź do kierownicy podczas renderowania:

var context= {
    'section1' : section1,
    'section2' : section2,
    'section1or2' : (section1)||(section2)
};

a następnie w szablonie kierownicy:

{{#if section1or2}}
    .. content
{{/if}}

Wspominam o tym dla uproszczenia, a także dlatego, że jest to odpowiedź, która może być szybka i pomocna przy jednoczesnym zachowaniu logicznej natury Kierownic.


3

Zainstaluj dodatek Ember Truth Helpers , uruchamiając poniższe polecenie

ember zainstaluj ember-true-helpers

możesz zacząć używać większości operatorów logicznych (eq, not-eq, not, i, lub gt, gte, lt, lte, xor).

{{#if (or section1 section2)}}  
...content  
{{/if}}

Możesz nawet dołączyć podwyrażenie, aby przejść dalej,

{{#if (or (eq section1 "section1") (eq section2 "section2") ) }}  
...content  
{{/if}}

2

Kolejne krzywe rozwiązanie dla trójskładnika:

'?:' ( condition, first, second ) {
  return condition ? first : second;
}

<span>{{?: fooExists 'found it' 'nope, sorry'}}</span>

Lub prosty pomocnik koalesce:

'??' ( first, second ) {
  return first ? first : second;
}

<span>{{?? foo bar}}</span>

Ponieważ te postacie nie mają specjalnego znaczenia w znacznikach kierownicy, możesz ich używać do nazw pomocników.


1

Znalazłem pakiet npm wykonany przy pomocy CoffeeScript, który zawiera wiele niesamowicie przydatnych pomocników dla Kierownic. Przejrzyj dokumentację pod następującym adresem URL:

https://npmjs.org/package/handlebars-helpers

Możesz zrobić to, wget http://registry.npmjs.org/handlebars-helpers/-/handlebars-helpers-0.2.6.tgzaby je pobrać i zobaczyć zawartość pakietu.

Będziesz w stanie robić rzeczy takie jak {{#is number 5}}lub{{formatDate date "%m/%d/%Y"}}


1

jeśli chcesz tylko sprawdzić, czy jest obecny jeden lub drugi element, możesz użyć tego niestandardowego pomocnika

Handlebars.registerHelper('if_or', function(elem1, elem2, options) {
  if (Handlebars.Utils.isEmpty(elem1) && Handlebars.Utils.isEmpty(elem2)) {
    return options.inverse(this);
  } else {
    return options.fn(this);
  }
});

lubię to

{{#if_or elem1 elem2}}
  {{elem1}} or {{elem2}} are present
{{else}}
  not present
{{/if_or}}

jeśli potrzebujesz również mieć „lub” do porównania wartości zwracanych przez funkcję , wolę dodać inną właściwość, która zwraca pożądany wynik.

Szablony powinny być mimo wszystko logiczne!



1

Właśnie przyszedł do tego postu z wyszukiwarki Google, w jaki sposób sprawdzić, czy ciąg znaków jest równy innym ciągom znaków.

Korzystam z HandlebarsJS po stronie serwera NodeJS, ale używam również tych samych plików szablonów w interfejsie, używając wersji HandlebarsJS w przeglądarce do parsowania. Oznaczało to, że gdybym chciał niestandardowego pomocnika, musiałbym zdefiniować go w 2 oddzielnych miejscach lub przypisać funkcję do przedmiotowego obiektu - zbyt duży wysiłek !!

Ludzie zapominają o tym, że niektóre obiekty mają funkcje dziedziczenia, których można używać w szablonie wąsów. W przypadku ciągu:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match

An Array containing the entire match result and any parentheses-captured matched results; null if there were no matches.

Możemy użyć tej metody, aby zwrócić tablicę dopasowań lub nulljeśli nie znaleziono żadnych dopasowań. Jest to idealne, ponieważ przeglądanie dokumentacji HandlebarsJS http://handlebarsjs.com/builtin_helpers.html

You can use the if helper to conditionally render a block. If its argument returns false, undefined, null, "", 0, or [], Handlebars will not render the block.

Więc...

{{#if your_string.match "what_youre_looking_for"}} 
String found :)
{{else}}
No match found :(
{{/if}}

AKTUALIZACJA:

Po przetestowaniu we wszystkich przeglądarkach nie działa to w przeglądarce Firefox . HandlebarsJS przekazuje inne argumenty do wywołania funkcji, co oznacza, że ​​po wywołaniu String.prototype.match drugi argument (tj. Flagi Regexp dla wywołania funkcji dopasowania zgodnie z powyższą dokumentacją) wydaje się być przekazywany. Firefox postrzega to jako przestarzałe użycie String.prototype.match i dlatego się psuje.

Obejściem tego problemu jest zadeklarowanie nowego funkcjonalnego prototypu dla obiektu String JS i użycie go zamiast tego:

if(typeof String.includes !== 'function') {
    String.prototype.includes = function(str) {
        if(!(str instanceof RegExp))
            str = new RegExp((str+'').escapeRegExp(),'g');
        return str.test(this);
    }
}

Upewnij się, że ten kod JS jest uwzględniony przed uruchomieniem funkcji Handlebars.compile (), a następnie w szablonie ...

{{#your_string}}
    {{#if (includes "what_youre_looking_for")}} 
        String found :)
    {{else}}
        No match found :(
    {{/if}}
{{/your_string}}

Zaktualizowano dla Firefoksa, tego właśnie używam
Jon

1

Tutaj mamy waniliowe kierownice do wielu logicznych && i || (i lub):

Handlebars.registerHelper("and",function() {
    var args = Array.prototype.slice.call(arguments);
    var options = args[args.length-1];

    for(var i=0; i<args.length-1; i++){
        if( !args[i] ){
            return options.inverse(this);
        }
    }

    return options.fn(this);
});


Handlebars.registerHelper("or",function() {
    var args = Array.prototype.slice.call(arguments);
    var options = args[args.length-1];

    for(var i=0; i<args.length-1; i++){
        if( args[i] ){
            return options.fn(this);
        }
    }

    return options.inverse(this);
}

// Results
// {{#and foo bar sally bob}} yup {{else}} nope {{/and}} // yup
// {{#or foo bar "" sally bob}} yup {{else}} nope {{/or}} // yup

// {{#and foo bar "" sally bob}} yup {{else}} nope {{/and}} // nope
// {{#or "" "" "" "" ""}} yup {{else}} nope {{/or}} // nope

Nie jestem pewien, czy można „bezpiecznie” używać „i” i „lub” ... może zmienić na coś takiego jak „op_and” i „op_or”?


1

Prawidłowe rozwiązanie dla AND / OR

Handlebars.registerHelper('and', function () {
    // Get function args and remove last one (function name)
    return Array.prototype.slice.call(arguments, 0, arguments.length - 1).every(Boolean);
});
Handlebars.registerHelper('or', function () {
    // Get function args and remove last one (function name)
    return Array.prototype.slice.call(arguments, 0, arguments.length - 1).some(Boolean);
}); 

Następnie zadzwoń w następujący sposób

{{#if (or (eq questionType 'STARTTIME') (eq questionType 'ENDTIME') (..) ) }}

BTW: Zauważ, że podane tutaj rozwiązanie jest nieprawidłowe, nie odejmuje ostatniego argumentu, który jest nazwą funkcji. https://stackoverflow.com/a/31632215/1005607

Jego oryginalny AND / OR opierał się na pełnej liście argumentów

   and: function () {
        return Array.prototype.slice.call(arguments).every(Boolean);
    },
    or: function () {
        return Array.prototype.slice.call(arguments).some(Boolean);
    }

Czy ktoś może zmienić tę odpowiedź? Właśnie straciłem godzinę, próbując naprawić coś w odpowiedzi zalecanej przez 86 osób. Rozwiązaniem jest odfiltrowanie ostatniego argumentu, który jest nazwą funkcji.Array.prototype.slice.call(arguments, 0, arguments.length - 1)


0

Postępując zgodnie z tymi 2 przewodnikami w sposób pozwalający użytkownikom na definiowanie niestandardowych instrukcji powiązanych z instrukcją i niestandardowych powiązanych pomocników , mogłem dostosować moje udostępnione widoki w tym poście przy stosie przepływu, aby użyć tego zamiast standardowego # instrukcja if. To powinno być bezpieczniejsze niż tylko wrzucanie tam #if.

Pomocnicy związani w niestandardach w tym systemie są wyjątkowi.

<li>
    <a href="{{unbound view.varProductSocialBlog}}">
        {{#if-equal view.showDiv "true"}}<div>{{/if-equal}}<i class="fa fa-rss-square"></i>{{#if-equal view.showDiv "true"}}</div>{{/if-equal}}
        {{#if-equal view.showTitle "true"}}Blog{{/if-equal}}
    </a>
</li>

Korzystam z ember cli projektu do budowy aplikacji ember.

Obecna konfiguracja w momencie pisania tego postu:

DEBUG: -------------------------------
DEBUG: Ember      : 1.5.1
DEBUG: Ember Data : 1.0.0-beta.7+canary.b45e23ba
DEBUG: Handlebars : 1.3.0
DEBUG: jQuery     : 2.1.1
DEBUG: -------------------------------

0

W Ember.js możesz użyć inline if helper in if block helper. Może zastąpić ||operator logiczny, na przykład:

{{#if (if firstCondition firstCondition secondCondition)}}
  (firstCondition || (or) secondCondition) === true
{{/if}}

0

Możesz to zrobić po prostu za pomocą operatora logicznego, takiego jak pokazano poniżej:

{{#if (or(eq firstValue 'String_to_compare_value') (eq secondValue 'String_to_compare_value'))}}business logic goes here{{/if}}

{{#if (and(eq firstValue 'String_to_compare_value') (eq secondValue 'String_to_compare_value'))}}business logic goes here{{/if}}

Przed zamknięciem, jeśli potrafisz napisać logikę biznesową


0

Możesz użyć następującego kodu:

{{#if selection1}}
    doSomething1
{{else}}
   {{#if selection2}}
       doSomething2
   {{/if}}
{{/if}}

Wyjaśnij więcej o swoich przemyśleniach i procesie, ludzie mogą bardzo trudno zrozumieć twoje rozwiązanie, jeśli nie znają kontekstu lub nie znają języka.
mrhn

-1

Oto podejście, którego używam dla ember 1.10 i ember-cli 2.0.

// app/helpers/js-x.js
export default Ember.HTMLBars.makeBoundHelper(function (params) {
  var paramNames = params.slice(1).map(function(val, idx) { return "p" + idx; });
  var func = Function.apply(this, paramNames.concat("return " + params[0] + ";"))
  return func.apply(params[1] === undefined ? this : params[1], params.slice(1));
});

Następnie możesz użyć go w swoich szablonach:

// used as sub-expression
{{#each item in model}}
  {{#if (js-x "this.section1 || this.section2" item)}}
  {{/if}}
{{/each}}

// used normally
{{js-x "p0 || p1" model.name model.offer.name}}

Gdzie argumenty do wyrażenia są przekazywane jako p0, p1, p2etc i p0może być także określany jako this.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.