Operador lógico em um handlebars.js {{#if}} condicional

Existe uma maneira no JS do guidão de incorporar operadores lógicos no operador condicional padrão handlebars.js? Algo assim:

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

Eu sei que eu poderia escrever meu próprio ajudante, mas primeiro eu gostaria de ter certeza de que não estou reinventando a roda.

Isso é possível “trapaceando” com um ajudante de bloco. Isso provavelmente vai contra a ideologia das pessoas que desenvolveram o Handlebars.

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

Você pode então chamar o ajudante no modelo como este

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

Levando a solução um passo adiante. Isso adiciona o operador de comparação.

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

Use-o em um modelo como este:

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

Versão do Café Script

 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 

Handlebars suporta operações aninhadas. Isso proporciona muita flexibilidade (e código mais limpo) se escrevermos nossa lógica de maneira um pouco diferente.

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

De fato, podemos adicionar todos os tipos de lógica:

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

Basta registrar esses ajudantes:

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

levando este para um entalhe, para aqueles de vocês que vivem no limite.

gist : https://gist.github.com/akhoury/9118682 Demo : Snippet de código abaixo

Guiador de {{#xif EXPRESSION}} {{else}} {{/xif}} : {{#xif EXPRESSION}} {{else}} {{/xif}}

um ajudante para executar uma instrução IF com qualquer expressão

  1. EXPRESSION é uma string devidamente escapada
  2. Sim, você precisa escutar corretamente os literais de string ou apenas aspas simples e duplas
  3. você pode acessar qualquer function ou propriedade global, ou seja, encodeURIComponent(property)
  4. Este exemplo assume que você passou este contexto para o seu template( {name: 'Sam', age: '20' } ) handlebars template( {name: 'Sam', age: '20' } ) , a age aviso é uma string , apenas para que eu possa demonstrar parseInt() mais tarde neste post

Uso:

 

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

Saída

 

BOOM

JavaScript: (depende de outra leitura de ajuda)

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

Guiador de guiador: {{x EXPRESSION}}

Um ajudante para executar expressões javascript

  1. EXPRESSION é uma string devidamente escapada
  2. Sim, você precisa escutar corretamente os literais de string ou apenas aspas simples e duplas
  3. você pode acessar qualquer function ou propriedade global, por exemplo, parseInt(property)
  4. este exemplo assume que você passou este contexto para o seu template( {name: 'Sam', age: '20' } ) handlebars template( {name: 'Sam', age: '20' } ) , age é uma string para propósitos de demo, pode ser qualquer coisa ..

Uso:

 

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

Saída:

 

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

JavaScript:

Isso parece um pouco grande porque eu estendi a syntax e comentei quase todas as linhas para fins de clareza

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

Existe uma maneira simples de fazer isso sem escrever uma function auxiliar … Isso pode ser feito dentro do modelo completamente.

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

Edit: Por outro lado, você pode fazer ou está fazendo isso:

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

Editar / Nota: No site do guidão: handlebarsjs.com aqui estão os valores falsos:

Você pode usar o auxiliar if para renderizar condicionalmente um bloco. Se o argumento retornar false, undefined, null, “” ou [] (um valor “falsy”), então qualquer ‘cond’ (como cond1 ou cond2) não será contado como verdadeiro.

Um problema com todas as respostas postadas aqui é que elas não funcionam com propriedades vinculadas, ou seja, a condição if não é reavaliada quando as propriedades envolvidas mudam. Aqui está uma versão um pouco mais avançada das ligações de suporte do auxiliar. Ele usa a function bind da fonte Ember, que também é usada para implementar o helper #if normal.

Este é limitado a uma única propriedade encadernada no lado esquerdo, comparando com uma constante no lado direito, o que eu acho que é bom o suficiente para a maioria dos propósitos práticos. Se você precisar de algo mais avançado do que uma simples comparação, então talvez seja bom começar declarando algumas propriedades calculadas e usando o #if auxiliar normal.

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

Você pode usá-lo assim:

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

Solução aprimorada que basicamente funciona com qualquer operador binário (pelo menos números, strings não funcionam bem com eval, CUIDAR DA INJEÇÃO POSSÍVEL DO SCRIPT SE USAR UM OPERADOR NÃO DEFINIDO COM ENTRADAS DE USUÁRIO):

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

Aqui está uma solução se você quiser verificar várias condições:

 /* 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': 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); }); 

Uso:

 /* if(list.length>0 && public){}*/ {{#checkIf list.length '>' 0 '&&' public '==' true}} 

condition satisfied

{{/checkIf}}

Aqui está um link para o ajudante de bloco que eu uso: auxiliar de bloco de comparação . Ele suporta todos os operadores padrão e permite que você escreva o código como mostrado abaixo. É realmente muito útil.

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

Semelhante à resposta de Jim, mas usando um pouco de criatividade, também poderíamos fazer algo assim:

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

Então, para usá-lo, temos algo como:

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

Sugiro mover o object para fora da function para melhor desempenho, mas caso contrário, você pode adicionar qualquer function de comparação desejada, incluindo “e” e “ou”.

Uma outra alternativa é usar o nome da function em #if . O #if detectará se o parâmetro é function e, se estiver, irá chamá-lo e usar seu retorno para verificação de integridade. Abaixo myFunction obtém contexto atual como this .

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

Instale o addon Ember Truth Helpers executando o comando abaixo

ember instalar ember-truth-helpers

você pode começar a usar a maioria dos operadores lógicos (eq, não-eq, não, e, ou, gt, gte, lt, lte, xor).

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

Você pode até include subexpressão para ir mais longe

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

Eu posso entender por que você gostaria de criar um ajudante para situações em que você tem um grande número de comparações variadas para executar dentro de seu modelo, mas para um número relativamente pequeno de comparações (ou mesmo uma, que foi o que me trouxe a esta página em primeiro lugar), provavelmente seria mais fácil definir uma nova variável de barras de guia em sua chamada de function de renderização de visualização, como:

Passe para os guidões no render:

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

e, em seguida, dentro do seu modelo de guiador:

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

Eu mencionei isso por simplicidade, e também porque é uma resposta que pode ser rápida e útil enquanto ainda está em conformidade com a natureza sem lógica do Handlebars.

Eu encontrei um pacote npm feito com o CoffeeScript que tem muitos ajudantes úteis incríveis para o Handlebars. Dê uma olhada na documentação no seguinte URL:

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

Você pode fazer um wget http://registry.npmjs.org/handlebars-helpers/-/handlebars-helpers-0.2.6.tgz para baixá-los e ver o conteúdo do pacote.

Você será capaz de fazer coisas como {{#is number 5}} ou {{formatDate date "%m/%d/%Y"}}

se você quiser apenas verificar se um ou outro elemento está presente, você pode usar esse ajudante personalizado

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

como isso

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

Se você também precisar de um “ou” para comparar os valores de retorno da function , prefiro adicionar outra propriedade que retorne o resultado desejado.

Os modelos devem ser sem lógica depois de tudo!

Infelizmente, nenhuma dessas soluções resolve o problema do operador “OR” “cond1 || cond2”.

  1. Verifique se o primeiro valor é verdadeiro
  2. Use “^” (ou) e verifique se de outra forma a cond2 é verdadeira

    {{# # cond1}} FAÇA A AÇÃO {{^}} {{#if cond2}} FAÇA A AÇÃO {{/ if}} {{/ if}}

Ele quebra a regra DRY. Então, por que não usar parcial para torná-lo menos confuso

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

Para aqueles com problemas na comparação das propriedades do object, dentro do auxiliar adicione essa solução

Ember.js helper não reconhece corretamente um parâmetro

Acabou de chegar a este post de uma pesquisa no google sobre como verificar se uma string é igual a outra string.

Eu uso o HandlebarsJS no lado do servidor do NodeJS, mas também uso os mesmos arquivos de modelo no front-end usando a versão do navegador do HandlebarsJS para analisá-lo. Isso significava que, se eu quisesse um ajudante personalizado, teria que defini-lo em dois lugares separados ou atribuir uma function ao object em questão – muito esforço!

O que as pessoas esquecem é que certos objects têm funções herdadas que podem ser usadas no modelo de bigode. No caso de uma string:

https://developer.mozilla.org/pt-BR/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.

Podemos usar esse método para retornar uma matriz de correspondências ou null se nenhuma correspondência for encontrada. Isso é perfeito, porque observamos a documentação do 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.

Assim…

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

ATUALIZAR:

Depois de testar em todos os navegadores, isso não funciona no Firefox . HandlebarsJS passa outros argumentos para uma chamada de function, o que significa que quando String.prototype.match é chamado, o segundo argumento (isto é, os sinalizadores Regexp para a chamada de function de correspondência conforme a documentação acima) parece estar sendo passado. O Firefox vê isso como um uso reprovado de String.prototype.match e, portanto, quebra.

Uma solução alternativa é declarar um novo protótipo funcional para o object String JS e usá-lo:

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

Assegure-se de que este código JS esteja incluído antes de executar sua function Handlebars.compile () e, em seguida, no seu modelo …

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

Aqui nós temos guidões de baunilha para múltiplos && e lógicos || (e ou):

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

Não tenho certeza se é "seguro" usar "e" e "ou" ... talvez mudar para algo como "op_and" e "op_or"?

Seguindo esses dois guias, a-way-to-let-users-define-custom-made-bound-if-statements e custom bound willers , consegui ajustar minhas visualizações compartilhadas nesta postagem em stackoverflow para usar isso em vez do padrão # se declaração. Isso deve ser mais seguro do que apenas jogar um #if lá.

Os assistentes customizados nessa essência são excelentes.

 
  • {{#if-equal view.showDiv "true"}}
    {{/if-equal}}{{#if-equal view.showDiv "true"}}
    {{/if-equal}} {{#if-equal view.showTitle "true"}}Blog{{/if-equal}}
  • Eu estou usando o projeto ember cli para construir meu aplicativo ember.

    Configuração atual no momento desta postagem:

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

    Em Ember.js você pode usar inline se o ajudante estiver em um ajudante de bloco. Pode replace || operador lógico, por exemplo:

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

    Você pode fazer isso simplesmente usando o operador lógico como mostrado abaixo:

     {{#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}} 

    Antes de fechar, você pode escrever sua lógica de negócios

    Solução Correta para 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); }); 

    Em seguida, ligue da seguinte forma

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

    BTW: Note que a solução dada aqui está incorreta, ele não está subtraindo o último argumento que é o nome da function. https://stackoverflow.com/a/31632215/1005607

    Seu original AND / OR foi baseado na lista completa de argumentos

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

    Alguém pode mudar essa resposta? Eu só perdi uma hora tentando consertar algo em uma resposta recomendada por 86 pessoas. A correção é filtrar o último argumento que é o nome da function. Array.prototype.slice.call(arguments, 0, arguments.length - 1)

    Ainda outra solução torta para um ajudante ternário:

     '?:' ( condition, first, second ) { return condition ? first : second; } {{?: fooExists 'found it' 'nope, sorry'}} 

    Ou um simples ajudante de colagem:

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

    Como esses caracteres não têm um significado especial na marcação dos guidões, você está livre para usá-los como nomes auxiliares.

    Aqui está uma abordagem que estou usando para o ember 1.10 e o 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)); }); 

    Então você pode usá-lo em seus modelos como este:

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

    Onde os argumentos para a expressão são passados ​​como p0 , p1 , p2 etc e p0 também podem ser referenciados como this .