Handlebars.js: Posso usar vários helpers em uma única tag?

Criado em 7 set. 2012  ·  17Comentários  ·  Fonte: handlebars-lang/handlebars.js

como {{ helper1 helper2 text }} , você sabe que às vezes apenas um ajudante não é suficiente para fazer o trabalho.

Comentários muito úteis

isso parece ter suporte nativo usando algo como:

{{ helper1 (helper2 text) }}

Todos 17 comentários

Eu acho que deveria haver uma maneira de aninhar expressões, como esta: {{headerText {{getTitle "my_page"}}}} .

Isso não é compatível no momento e não tenho planos de oferecer suporte.

No entanto, você poderia teoricamente criar um ajudante que consome intencionalmente e acorrenta outros ajudantes:

{{chain "helper1" "helper2" text}}

Se alguém estiver interessado, criei um ajudante:

Handlebars.registerHelper('chain', function () {
    var helpers = [], value;
    $.each(arguments, function (i, arg) {
        if (Handlebars.helpers[arg]) {
            helpers.push(Handlebars.helpers[arg]);
        } else {
            value = arg;
            $.each(helpers, function (j, helper) {
                value = helper(value, arguments[i + 1]);
            });
            return false;
        }
    });
    return value;
});

Funciona assim:

{{chain "taxAdd" "formatPrice" this.product.price}}

@Znarkus que introduz uma dependência para jQuery

@jrajan Eu só usei jQuery.each , sinta-se à vontade para reescrever com quaisquer que sejam suas preferências.

Se alguém estiver interessado em uma versão agnóstica do jQuery:

Handlebars.registerHelper('chain', function() {
  var helpers = [];
  var args = Array.prototype.slice.call(arguments);
  var argsLength = args.length;
  var index;
  var arg;

  for (index = 0, arg = args[index];
       index < argsLength;
       arg = args[++index]) {
    if (Handlebars.helpers[arg]) {
      helpers.push(Handlebars.helpers[arg]);
    } else {
      args = args.slice(index);
      break;
    }
  }

  while (helpers.length) {
    args = [helpers.pop().apply(Handlebars.helpers, args)];
  }

  return args.shift();
});

Um problema com ambas as implementações é digno de nota: se algum dos argumentos tiver a intenção de serem valores a serem passados ​​para um auxiliar, mas também forem (coercíveis para) strings que correspondem ao nome de um auxiliar existente, haverá resultados inesperados.

Aqui estão duas implementações. Ambos permitem que você tenha vários argumentos enviados para cada auxiliar, ao contrário dos exemplos anteriores.
Eles também são escritos em script de café e dependem de sublinhado ou lodash.

O primeiro permite que você faça algo assim:
{{{chain 'join-strings' 'link-twitter-handles' '@' twitterUsername}}}
Mas algo assim produziria resultados inesperados:
{{{chain 'join-strings' 'link-twitter-handles' '@' 'join-strings' twitterUsername}}}

Handlebars.registerHelper 'chain', ->
    # Get rid of the options hash
    args = Array.prototype.slice.call arguments, 0, -1
    helpers = []
    argsForHelpers = null
    value = undefined

    _.each args, (arg, i) ->
        if Handlebars.helpers[arg]
            helpers.push Handlebars.helpers[arg]
        else if not value # Only call the helpers once
            value = arg
            unless argsForHelpers
                argsForHelpers = args[i+1..-1]
                argsForHelpers.unshift value
            _.each helpers, (helper) ->
                argsForHelpers[0] = value
                value = helper.apply null, argsForHelpers

    value

Este segundo exemplo tem um separador que permite chain separar os auxiliares dos argumentos.
O auxiliar irá assumir que cada argumento antes do separador é um auxiliar e todos os outros devem ser passados ​​como argumentos para os auxiliares.

Handlebars.registerHelper 'chain', ->
    # Get rid of the options hash
    args = Array.prototype.slice.call arguments, 0, -1
    helpers = []

    for arg,i in args
        if arg is '--'
            argsForHelpers = args.slice i + 1
            value = argsForHelpers[0]
            break
        else
            helpers.push Handlebars.helpers[arg]

    _.each helpers, (helper) ->
        argsForHelpers[0] = value
        value = helper.apply null, argsForHelpers

    value

Dado este modelo:
{{{chain 'join-strings' 'link-twitter-handles' '@' 'join-strings' twitterUsername}}}
e este objeto:
{twitterUsername: 'abc'}
Poderíamos esperar um modelo compilado como este:
<a href="https://twitter.com/join-stringsabc">@join-stringsabc</a>

Peguei a implementação de

/**
 * Takes an arbitrary number of arguments, the first of which is the operation type 'AND' or 'OR'.
 * Following that will be a list of block helper names prefixed by '!!'.
 * Calls each block helper with the remaining arguments.
 *
 * <strong i="7">@returns</strong> {string}  returns options.fn(this) or options.inverse(this) depending on result of each helper and operation type
 */
Handlebars.registerHelper('chainBlockHelpers', function() {
    var index, arg, helperResult, pass,
        helpers = [],
        args = Array.prototype.slice.call(arguments),
        argsLength = args.length,
        options = args[argsLength-1],
        operation = args.shift(),
        passVal = options.fn(this),
        failVal = options.inverse(this);

    if (operation !== 'AND' && operation !== 'OR')
        throw new Error ('chainBlockHelpers only supports "AND" or "OR" operations.')

    for (index = 0, arg = args[index]; index < argsLength; arg = args[++index]) {
        if (typeof arg == 'string' && arg.startsWith('!!') && Handlebars.helpers[arg.substr(2)]) {
            helpers.push(Handlebars.helpers[arg.substr(2)]);
        } else {
            args = args.slice(index);
            break;
        }
    }

    if (operation === 'AND') {
        pass = true;
        while (helpers.length) {
            helperResult = helpers.pop().apply(Handlebars.helpers, args);
            if (helperResult == failVal) {
                pass = false;
                break;
            }
        }
    } else {
        pass = false;
        while (helpers.length) {
            helperResult = helpers.pop().apply(Handlebars.helpers, args);
            if (helperResult == passVal) {
                pass = true;
                break;
            }
        }
    }

    return pass ? passVal : failVal;
});

isso parece ter suporte nativo usando algo como:

{{ helper1 (helper2 text) }}

Em alguns casos, você pode fazer seus ajudantes trabalharem como:

{{#helper1}}{{helper2}}content{{/helper2}}{{/helper1}}

@Znarkus Usando seu método, quando passo em dois auxiliares que têm strings seguras aplicadas, minhas quebras de código. Na primeira vez que passo os dados, ele vem como uma string, mas no segundo filtro, o valor que está sendo passado parece ser um objeto? No meu depurador, ele é mostrado como 'uma quebra de linha e, em seguida, string:' meu valor '; Não tenho certeza do que SafeString faz com o valor, mas passar por ele duas vezes não parece funcionar muito bem.

    Handlebars.registerHelper('shortNumber', function (value) {
        //return new Handlebars.SafeString(iSpot.number.shortNumber(value)); // Breaks
        return iSpot.number.shortNumber(value);  // Works
    });
    Handlebars.registerHelper('asDollars', function (value) {
        //return new Handlebars.SafeString(iSpot.number.asDollars(value)); // Breaks
        return iSpot.number.asDollars(value); // Works
    });

Abordagem +1 {{pluralize (titleize (humanize schema.name))}}

+1 que parece uma sintaxe limpa e agradável.

+1 @amwmedia Funciona perfeitamente, obrigado

A abordagem @amwmedia funciona como um charme, mas apenas sem Handlebars.SafeString() como @cssagogo mencionado acima.

Então você pode fazer smth. Como:

{{> partial text=(concat value (default extension 'PDF')) }}

obrigado

Eu corri para esse problema sozinho. Sei que estou muito atrasado nisso, mas tenho uma solução que funcionou para mim. Por favor, me perdoe se isso já foi mencionado (não tive tempo para verificar). Esta não é a solução mais legal e, tecnicamente, não é uma resposta direta à sua pergunta, mas é uma solução para o problema (executando várias funções em um valor).

Eu mantenho todas as minhas funções auxiliares em um arquivo separado. Como estão todos no mesmo lugar, posso passar o valor para um auxiliar e, em seguida, apenas chamar todas as outras funções que tenho no arquivo da função auxiliar. Assim:

{{ function1 value }}

arquivo auxiliar

function2 (value) {
   // code
}

function1 (value) {
   // code
   function2(value)
   // code
   return value;
}

Obviamente, esta é uma maneira simples de poder usar quantas funções desejar. Você pode até designar uma função de base que atua como seu "encadeador".

{{ chainer value }}

arquivo auxiliar

function1 (value) {
   // code
}
function2 (value) {
   // code
}
function3 (value) {
   // code
}
function4 (value) {
   // code
}

function chainer(value) {
   function1(value)
   function2(value)
   function3(value)
   function4(value)
   return value;
}

Eu mesmo não fiz isso, mas não vejo por que não funcionaria.

Nota Você só precisa registrar as funções que deseja usar em seu html como ajudantes do guiador. Eu pessoalmente tenho todos os meus registrados porque os utilizo independentemente uns dos outros, mas você não precisa se não se prevê a necessidade de usá-los diretamente.

Um retrocesso à programação procedural :)

Esta página foi útil?
0 / 5 - 0 avaliações