tl; dr: ¿Es posible hacer un literal de plantilla reutilizable?
He intentado usar plantillas literales, pero supongo que no lo entiendo y ahora me estoy frustrando. Quiero decir, creo que lo entiendo, pero "eso" no debería ser cómo funciona, o cómo debería ser. Debería ser diferente.
Todos los ejemplos que veo (incluso las plantillas etiquetadas) requieren que las "sustituciones" se realicen en tiempo de declaración y no en tiempo de ejecución, lo que me parece completamente inútil para una plantilla. Tal vez esté loco, pero una "plantilla" para mí es un documento que contiene tokens que se sustituyen cuando lo usas, no cuando lo creas, de lo contrario es solo un documento (es decir, una cadena). Una plantilla se almacena con los tokens como tokens y esos tokens se evalúan cuando ... la evalúas.
Todo el mundo cita un ejemplo horrible similar a:
var a = 'asd';
return `Worthless ${a}!`
Eso está bien, pero si ya lo sé a
, lo haría simplemente return 'Worthless asd'
o return 'Worthless '+a
. ¿Cuál es el punto de? Seriamente. Está bien, el punto es la pereza; menos ventajas, más legibilidad. Excelente. ¡Pero eso no es una plantilla! No en mi humilde opinión. ¡Y MHO es todo lo que importa! El problema, en mi humilde opinión, es que la plantilla se evalúa cuando se declara, así que, si lo hace, en mi humilde opinión:
var tpl = `My ${expletive} template`;
function go() { return tpl; }
go(); // SPACE-TIME ENDS!
Como expletive
no se declara, genera algo como My undefined template
. Súper. En realidad, al menos en Chrome, ni siquiera puedo declarar la plantilla; arroja un error porque expletive
no está definido. Lo que necesito es poder hacer la sustitución después de declarar la plantilla:
var tpl = `My ${expletive} template`;
function go() { return tpl; }
var expletive = 'great';
go(); // My great template
Sin embargo, no veo cómo esto es posible, ya que estas no son realmente plantillas. Incluso cuando dices que debería usar etiquetas, no, no funcionan:
> 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...
Todo esto me ha llevado a creer que los literales de plantilla están horriblemente mal nombrados y deberían llamarse como realmente son: heredocs . ¿Supongo que la parte "literal" debería haberme alertado (como en, inmutable)?
¿Me estoy perdiendo de algo? ¿Existe una (buena) manera de hacer literal una plantilla reutilizable?
Te doy, plantillas literales reutilizables :
> 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!
Y aquí hay una función de "ayuda" ingenua ...
function t(t) { return '`'+t.replace('{','${')+'`'; }
var template = t(`This is
my {expletive} reusable
template!`);
...para hacerlo mejor".
Me inclino a llamarlos guías de plantilla por el área desde la que producen sensaciones retorcidas.
<strike>
etiqueta.Respuestas:
Para que estos literales funcionen como otros motores de plantilla, debe haber un formulario intermedio.
La mejor manera de hacer esto es usar el
Function
constructor.Al igual que con otros motores de plantillas, puede obtener esa cadena de otros lugares, como un archivo.
Puede haber problemas al usar este método, como que las etiquetas de plantilla son difíciles de usar, pero se pueden agregar si eres inteligente. Tampoco puede tener lógica JavaScript en línea debido a la interpolación tardía. Esto también se puede remediar con un poco de pensamiento.
fuente
new Function(`return \`${template}\`;`)
Puede poner una cadena de plantilla en una función:
Puedes hacer lo mismo con una plantilla etiquetada:
La idea es dejar que el analizador de plantillas separe las cadenas constantes de las "ranuras" de la variable y luego devuelva una función que vuelva a unirlo todo en función de un nuevo conjunto de valores cada vez.
fuente
reusable
podría implementarse para que devuelva una función, y usarías${0}
y${1}
dentro del literal en lugar de${a}
y${b}
. Luego puede usar esos valores para referirse a los argumentos de la función, similar a lo que hace Bergi en su último ejemplo: stackoverflow.com/a/22619256/218196 (o supongo que es básicamente lo mismo).expression`a + ${node}`
para construir un nodo BinaryExpression con un nodo AST existentenode
. Internamente insertamos un marcador de posición para generar un código válido, lo analizamos como un AST y reemplazamos el marcador de posición con el valor pasado.Probablemente la forma más limpia de hacer esto es con las funciones de flecha (porque en este punto, ya estamos usando ES6)
... Y para literales de plantilla etiquetados:
Esto también evita el uso de
eval()
oFunction()
que puede causar problemas con los compiladores y causar mucha ralentización.fuente
myTag
para hacer algunas cosas. Por ejemplo, utilice los parámetros de entrada como clave para almacenar en caché la salida.var reusable = (value: string) => `Value is ${value}`
.2019 respuesta :
Nota : La biblioteca originalmente esperaba que los usuarios desinfectaran las cadenas para evitar XSS. La versión 2 de la biblioteca ya no requiere que se desinfecten las cadenas de usuario (lo que los desarrolladores web deberían hacer de todos modos), ya que evita
eval
completo.El
es6-dynamic-template
módulo de npm hace esto.A diferencia de las respuestas actuales:
this
en la cadena de la plantillaEl uso es simple. Utilice comillas simples ya que la cadena de la plantilla se resolverá más adelante.
fuente
10 * 20 = ${10 * 20}
para que sea un formato similar, pero no es ni remotamente literal de plantillaSí, puede hacerlo analizando su cadena con la plantilla como JS por
Function
(oeval
), pero esto no se recomienda y permite el ataque XSSMostrar fragmento de código
En su lugar, puede insertar de forma segura
obj
campos de objeto en la plantillastr
de forma dinámica de la siguiente maneraMostrar fragmento de código
fuente
.*?
medios no codiciosos : si elimina"?"
, el fragmento dará un resultado incorrectofunction 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 la respuesta proporcionada por @metamorphasi;
fuente
eval
.var hosting
) AQUÍ .Si no desea utilizar los parámetros ordenados o contexto / espacios de nombres para hacer referencia a las variables en su plantilla, por ejemplo
${0}
,${this.something}
o${data.something}
, puede tener una función de plantilla que se encarga de la definición del alcance para usted.Ejemplo de cómo podría llamar a una plantilla de este tipo:
La función de plantilla:
La peculiaridad en este caso es que solo tiene que pasar una función (en el ejemplo, usé una función de flecha) que devuelve el literal de la plantilla ES6. Creo que es una compensación menor obtener el tipo de interpolación reutilizable que buscamos.
Aquí está en GitHub: https://github.com/Adelphos/ES6-Reuseable-Template
fuente
Object.values()
yObject.keys()
La respuesta corta es simplemente usar _.template en lodash
fuente
Tal vez me esté perdiendo algo, porque mi solución a este problema me parece tan obvia que estoy muy sorprendido de que nadie haya escrito eso ya en una pregunta tan antigua.
Tengo casi una línea para eso:
Eso es todo. Cuando quiero reutilizar una plantilla y diferir la resolución de las sustituciones, simplemente hago:
La aplicación de esta etiqueta devuelve un
'function'
(en lugar de a'string'
) que ignora cualquier parámetro pasado al literal. Entonces se puede llamar con nuevos parámetros más tarde. Si un parámetro no tiene un reemplazo correspondiente, se convierte en'undefined'
.Respuesta extendida
Este código simple es funcional, pero si necesita un comportamiento más elaborado, se puede aplicar esa misma lógica y hay infinitas posibilidades. Tú podrías:
Puede almacenar los valores originales pasados al literal en la construcción y usarlos de manera creativa al aplicar la plantilla. Podrían convertirse en banderas, validadores de tipos, funciones, etc. Este es un ejemplo que los usa como valores predeterminados:
Luego:
Hazlo envolviendo esta lógica en una función que espera, como argumento, una función personalizada que se pueda aplicar en la reducción (al unir las piezas del literal de la plantilla) y devuelve una nueva plantilla con comportamiento personalizado.
Luego, podría, por ejemplo, escribir plantillas que escapen automáticamente o desinfecten parámetros al escribir html, css, sql, bash incrustados ...
Con esta plantilla sql ingenua (repito, ingenua! ) Podríamos construir consultas como esta:
Aceptar parámetros nombrados para la sustitución: un ejercicio no tan difícil, basado en lo que ya se dio. Hay una implementación en esta otra respuesta .
Hacer que el objeto de retorno se comporte como
'string'
: Bueno, esto es controvertido, pero podría conducir a resultados interesantes. Se muestra en esta otra respuesta .Resuelva los parámetros dentro del espacio de nombres global en el sitio de la llamada:
Bueno, esto es lo que mostró OP es su adenda, utilizando el comando
, es decir,evil
eval
. Esto podría hacerse sineval
, simplemente buscando el nombre de la variable pasada en el objeto global (o ventana). No mostraré cómo hacerlo porque no me gusta. Los cierres son la elección correcta.fuente
Este es mi mejor intento:
Para generalizar:
Si no está ejecutando E6, también puede hacer:
Esto parece ser un poco más conciso que las respuestas anteriores.
https://repl.it/@abalter/reusable-JS-template-literal
fuente
En general, estoy en contra de usar el mal
eval()
, pero en este caso tiene sentido:Luego, si cambia los valores y llama a eval () nuevamente, obtiene el nuevo resultado:
Si lo desea en una función, entonces se puede escribir así:
fuente
eval
.eval()
explícitamente es exactamente lo mismo queeval()
, por lo tanto, no hay ningún beneficio en ello, ya que solo hace que el código sea más difícil de leer.populate
función no construye dinámicamente el código, no debería usarseeval
con todos sus inconvenientes.function populate(a,b) { return `${a}.${b}`; }
eval no agrega nadaACTUALIZADO: La siguiente respuesta está limitada a nombres de variables individuales, por lo que las plantillas como:
'Result ${a+b}'
no son válidas para este caso. Sin embargo, siempre puedes jugar con los valores de la plantilla:RESPUESTA ORIGINAL:
Basado en las respuestas anteriores pero creando una función de utilidad más "amigable":
Puede invocarlo como:
Y la cadena resultante debería ser:
fuente
`Result: ${a+b}`
Si está buscando algo bastante simple (solo campos variables fijos, sin cálculos, condicionales ...) pero eso también funciona en el lado del cliente en navegadores sin soporte de cadenas de plantillas como IE 8,9,10,11 ...
aquí vamos:
fuente
Estaba molesto por la redundancia adicional necesario de escribir
this.
cada vez, por lo que también agregó expresiones regulares para expandir las variables como.a
athis.a
.Solución:
Utilizar como tal:
fuente
Solo publico un paquete npm que simplemente puede hacer este trabajo. Profundamente inspirado por esta respuesta .
Su implemento es mortalmente simple. Ojalá te guste.
fuente
puede usar la función de flecha en línea como esta, definición:
uso:
fuente
Cadena de plantilla de tiempo de ejecución
Prueba
fuente
fuente