tl; dr: É possível tornar literal um modelo reutilizável?
Tenho tentado usar literais de modelo, mas acho que não entendi e agora estou ficando frustrado. Quer dizer, acho que entendi, mas "isso" não deveria ser como funciona, ou como deveria ser. Deve ser diferente.
Todos os exemplos que vejo (mesmo os modelos marcados) exigem que as "substituições" sejam feitas no momento da declaração e não no tempo de execução, o que me parece totalmente inútil para um modelo. Talvez eu esteja louco, mas um "modelo" para mim é um documento que contém tokens que são substituídos quando você o usa, não quando você cria, caso contrário, é apenas um documento (ou seja, uma string). Um modelo é armazenado com os tokens como tokens e esses tokens são avaliados quando você ... avalia.
Todo mundo cita um exemplo horrível semelhante a:
var a = 'asd';
return `Worthless ${a}!`
Isso é bom, mas se eu já souber a
, eu apenas return 'Worthless asd'
ou return 'Worthless '+a
. Qual é o ponto? Seriamente. Ok, o ponto é preguiça; menos vantagens, mais legibilidade. Ótimo. Mas isso não é um modelo! Não IMHO. E MHO é tudo o que importa! O problema, IMHO, é que o modelo é avaliado quando é declarado, então, se o fizer, IMHO:
var tpl = `My ${expletive} template`;
function go() { return tpl; }
go(); // SPACE-TIME ENDS!
Uma vez que expletive
não é declarado, ele produz algo como My undefined template
. Super. Na verdade, pelo menos no Chrome, não consigo nem declarar o template; ele lança um erro porque expletive
não está definido. O que eu preciso é ser capaz de fazer a substituição após declarar o modelo:
var tpl = `My ${expletive} template`;
function go() { return tpl; }
var expletive = 'great';
go(); // My great template
No entanto, não vejo como isso é possível, já que esses não são realmente modelos. Mesmo quando você diz que devo usar tags, não, elas não funcionam:
> explete = function(a,b) { console.log(a); console.log(b); }
< function (a,b) { console.log(a); console.log(b); }
> var tpl = explete`My ${expletive} template`
< VM2323:2 Uncaught ReferenceError: expletive is not defined...
Tudo isso me levou a acreditar que os literais de modelo têm nomes horrivelmente errados e deveriam ser chamados do que realmente são: heredocos . Eu acho que a parte "literal" deveria ter me avisado (como em, imutável).
Estou esquecendo de algo? Existe uma (boa) maneira de tornar literal um modelo reutilizável?
Eu te dou, literais de modelo reutilizáveis :
> function out(t) { console.log(eval(t)); }
var template = `\`This is
my \${expletive} reusable
template!\``;
out(template);
var expletive = 'curious';
out(template);
var expletive = 'AMAZING';
out(template);
< This is
my undefined reusable
template!
This is
my curious reusable
template!
This is
my AMAZING reusable
template!
E aqui está uma função ingênua de "auxiliar" ...
function t(t) { return '`'+t.replace('{','${')+'`'; }
var template = t(`This is
my {expletive} reusable
template!`);
... para torná-lo "melhor".
Estou inclinado a chamá-los de gutais de modelo por causa da área a partir da qual eles produzem sentimentos tortuosos.
<strike>
tag.Respostas:
Para fazer esses literais funcionarem como outros mecanismos de modelo, é necessário que haja um formulário intermediário.
A melhor maneira de fazer isso é usar o
Function
construtor.Tal como acontece com outros motores de modelo, você pode obter essa string de outros lugares, como um arquivo.
Pode haver problemas ao usar esse método, como tags de modelo são difíceis de usar, mas eles podem ser adicionados se você for inteligente. Você também não pode ter lógica JavaScript embutida por causa da interpolação tardia. Isso também pode ser remediado com alguma reflexão.
fonte
new Function(`return \`${template}\`;`)
Você pode colocar uma string de modelo em uma função:
Você pode fazer a mesma coisa com um modelo com tag:
A ideia é permitir que o analisador de modelo separe as strings constantes da variável "slots" e, em seguida, retorne uma função que remenda tudo novamente com base em um novo conjunto de valores a cada vez.
fonte
reusable
pode ser implementado para que retorne uma função, e você usaria${0}
e${1}
dentro do literal em vez de${a}
e${b}
. Então, você pode usar esses valores para se referir aos argumentos da função, semelhante ao que Bergi faz em seu último exemplo: stackoverflow.com/a/22619256/218196 (ou acho que é basicamente o mesmo).expression`a + ${node}`
para construir um nó BinaryExpression com um nó AST existentenode
. Internamente, inserimos um marcador para gerar código válido, analisamos como um AST e substituímos o marcador com o valor passado.Provavelmente, a maneira mais limpa de fazer isso é com as funções de seta (porque, neste ponto, já estamos usando ES6)
... E para literais de modelo marcados:
Isso também evita o uso de
eval()
ouFunction()
que pode causar problemas com compiladores e causar muita lentidão.fonte
myTag
para fazer algumas coisas. Por exemplo, use os parâmetros de entrada como a chave para armazenar a saída em cache.var reusable = (value: string) => `Value is ${value}`
.Resposta de 2019 :
Nota : A biblioteca originalmente esperava que os usuários limpassem as strings para evitar XSS. A versão 2 da biblioteca não requer mais que as strings do usuário sejam higienizadas (o que os desenvolvedores da web devem fazer de qualquer maneira), pois evita
eval
completamente.O
es6-dynamic-template
módulo no npm faz isso.Ao contrário das respostas atuais:
this
da string do templateO uso é simples. Use aspas simples, pois a string do modelo será resolvida mais tarde!
fonte
10 * 20 = ${10 * 20}
assim, pode ser um formato semelhante, mas não é nem remotamente es6 literais de modeloSim, você pode fazer isso analisando sua string com o modelo como JS por
Function
(oueval
) - mas isso não é recomendado e permite ataques XSSExibir trecho de código
Em vez disso, você pode inserir campos de objeto com segurança
obj
no modelo destr
forma dinâmica da seguinte formaExibir trecho de código
fonte
.*?
significa não ganancioso - se você remover"?"
, o snippet fornecerá um resultado erradofunction taggedTemplate(template, data, matcher) { if (!template || !data) { return template; } matcher = matcher || /{(\w*)}/g; // {one or more alphanumeric characters with no spaces} return template.replace(matcher, function (match, key) { var value; try { value = data[key] } catch (e) { // } return value || ""; }); }
data = { a: 1, b: { c:2, d:3 } }
->b.c
?Simplificando a resposta fornecida por @metamorphasi;
fonte
eval
.var hosting
) AQUI .Se você não quiser usar os parâmetros ordenados ou contexto / namespaces para referenciar as variáveis no modelo, por exemplo
${0}
,${this.something}
ou${data.something}
, você pode ter uma função de modelo que cuida do escopo para você.Exemplo de como você pode chamar esse modelo:
A função de modelo:
A peculiaridade neste caso é que você só precisa passar uma função (no exemplo, usei uma função de seta) que retorna o literal de modelo ES6. Acho que é uma troca menor obter o tipo de interpolação reutilizável que buscamos.
Aqui está no GitHub: https://github.com/Adelphos/ES6-Reuseable-Template
fonte
Object.values()
eObject.keys()
A resposta curta é apenas usar _.template no lodash
fonte
Talvez eu seja faltando alguma coisa, porque minha solução para este problema me parece tão óbvia que estou muito surpreso que ninguém tenha escrito isso em uma pergunta tão antiga.
Eu tenho quase uma linha para isso:
Isso é tudo. Quando quero reutilizar um template e adiar a resolução das substituições, eu apenas faço:
Aplicar essa tag retorna um
'function'
(em vez de a'string'
) que ignora quaisquer parâmetros passados para o literal. Em seguida, ele pode ser chamado com novos parâmetros posteriormente. Se um parâmetro não tiver substituição correspondente, ele se tornará'undefined'
.Resposta estendida
Este código simples é funcional, mas se você precisa de um comportamento mais elaborado, essa mesma lógica pode ser aplicada e as possibilidades são infinitas. Você poderia:
Você pode armazenar os valores originais passados para o literal na construção e usá-los de maneiras criativas ao aplicar o modelo. Eles podem se tornar sinalizadores, validadores de tipo, funções etc. Este é um exemplo que os usa como valores padrão:
Então:
Faça isso envolvendo essa lógica em uma função que espera, como argumento, uma função customizada que pode ser aplicada na redução (ao juntar as partes do literal do template) e retorna um novo template com comportamento customizado.
Em seguida, você poderia, por exemplo, escrever modelos que escapam ou higienizam parâmetros automaticamente ao escrever html, css, sql, bash embutido ...
Com este modelo sql ingênuo (repito, ingênuo! ), Poderíamos construir consultas como esta:
Aceite os parâmetros nomeados para substituição: Um exercício não tão difícil, baseado no que já foi fornecido. Há uma implementação nesta outra resposta .
Faça com que o objeto de retorno se comporte como um
'string'
: Bem, isso é controverso, mas pode levar a resultados interessantes. Mostrado nesta outra resposta .Resolva os parâmetros dentro do namespace global no site de chamada:
Bem, isso é o que OP mostrou ser seu adendo, usando o comando
, quero dizerevil
eval
,. Isso poderia ser feito semeval
, apenas pesquisando o nome da variável passado no objeto global (ou janela). Não vou mostrar como faço porque não gosto. Os fechamentos são a escolha certa.fonte
Esta é minha melhor tentativa:
Para generalizar:
Se você não estiver executando o E6, também pode fazer:
Parece um pouco mais conciso do que as respostas anteriores.
https://repl.it/@abalter/reusable-JS-template-literal
fonte
Em geral, sou contra o mal
eval()
, mas neste caso faz sentido:Então, se você alterar os valores e chamar eval () novamente, obterá o novo resultado:
Se você quiser em uma função, pode ser escrito assim:
fonte
eval
.eval()
explicitamente é exatamente o mesmo queeval()
, portanto, não há nenhum benefício nisso, pois apenas torna o código mais difícil de ler.populate
função não constrói o código dinamicamente, ela não deve ser usadaeval
com todas as suas desvantagens.function populate(a,b) { return `${a}.${b}`; }
o eval não acrescenta nadaATUALIZADO: A resposta a seguir é limitada a nomes de variáveis únicas, portanto, modelos como:
'Result ${a+b}'
não são válidos para este caso. No entanto, você sempre pode brincar com os valores do modelo:RESPOSTA ORIGINAL:
Com base nas respostas anteriores, mas criando uma função de utilidade mais "amigável":
Você pode invocá-lo assim:
E a string resultante deve ser:
fonte
`Result: ${a+b}`
Se você está procurando por algo bastante simples (apenas campos de variáveis fixas, sem cálculos, condicionais ...), mas isso também funciona do lado do cliente em navegadores sem suporte de string de modelo como o IE 8,9,10,11 ...
aqui vamos nós:
fonte
Eu estava irritado com a redundância extra necessário de digitar
this.
cada vez, para que eu também acrescentou regex para expandir variáveis como.a
athis.a
.Solução:
Use como tal:
fonte
Acabei de publicar um pacote npm que pode simplesmente fazer esse trabalho. Profundamente inspirado por esta resposta .
Seu implemento é mortalmente simples. Desejo que você goste.
fonte
você pode usar a função de seta em linha como esta, definição:
uso:
fonte
String de modelo de tempo de execução
Teste
fonte
fonte