Les littéraux de modèle ES6 peuvent-ils être substitués à l'exécution (ou réutilisés)?


129

tl; dr: Est-il possible de rendre un modèle réutilisable littéral?

J'ai essayé d'utiliser des modèles littéraux, mais je suppose que je ne comprends tout simplement pas et que maintenant je suis frustré. Je veux dire, je pense que je comprends, mais "ça" ne devrait pas être comment ça marche, ni comment ça devrait arriver. Cela devrait être différent.

Tous les exemples que je vois (même les modèles étiquetés) nécessitent que les "substitutions" soient effectuées au moment de la déclaration et non au moment de l'exécution, ce qui me semble totalement inutile pour un modèle. Peut-être que je suis fou, mais un "modèle" pour moi est un document qui contient des jetons qui sont substitués lorsque vous l'utilisez, pas lorsque vous le créez, sinon c'est juste un document (c'est-à-dire une chaîne). Un modèle est stocké avec les jetons sous forme de jetons et ces jetons sont évalués lorsque vous ... l'évaluez.

Tout le monde cite un exemple horrible semblable à:

var a = 'asd';
return `Worthless ${a}!`

C'est bien, mais si je sais déjà a, je le ferais simplement return 'Worthless asd'ou return 'Worthless '+a. À quoi ça sert? Sérieusement. Ok, le point est la paresse; moins d'avantages, plus de lisibilité. Génial. Mais ce n'est pas un modèle! Pas à mon humble avis. Et MHO est tout ce qui compte! Le problème, à mon humble avis, est que le modèle est évalué lorsqu'il est déclaré, donc, si vous le faites, à mon humble avis:

var tpl = `My ${expletive} template`;
function go() { return tpl; }
go(); // SPACE-TIME ENDS!

Puisque expletiven'est pas déclaré, il génère quelque chose comme My undefined template. Super. En fait, dans Chrome au moins, je ne peux même pas déclarer le modèle; il jette une erreur car expletiven'est pas défini. Ce dont j'ai besoin, c'est de pouvoir effectuer la substitution après avoir déclaré le modèle:

var tpl = `My ${expletive} template`;
function go() { return tpl; }
var expletive = 'great';
go(); // My great template

Cependant, je ne vois pas comment cela est possible, car ce ne sont pas vraiment des modèles. Même quand vous dites que je devrais utiliser des balises, non, elles ne fonctionnent pas:

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

Tout cela m'a conduit à croire que les littéraux de modèle sont horriblement mal nommés et devraient être appelés ce qu'ils sont vraiment: heredocs . Je suppose que la partie "littérale" aurait dû me prévenir (comme dans, immuable)?

Est-ce que je manque quelque chose? Existe-t-il un (bon) moyen de rendre littéral un modèle réutilisable?


Je vous donne, des littéraux de modèle réutilisables :

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

Et voici une fonction "helper" naïve ...

function t(t) { return '`'+t.replace('{','${')+'`'; }
var template = t(`This is
my {expletive} reusable
template!`);

...Pour le faire mieux".

J'ai tendance à les appeler des guterals modèles en raison de la zone à partir de laquelle ils produisent des sentiments sinueux.


1
Il prend en charge le barré (mais pas dans les commentaires comme celui-ci). Mettez votre texte dans une <strike>balise.
Pointy

Les littéraux de modèle ES6 sont principalement destinés à l'interpolation de chaîne à l'ancienne. Si vous voulez des modèles dynamiques, utilisez Handlebars, etc., ou la solution de modèle balisé de Pointy.
joews

1
Les chaînes de modèle ne sont, malgré le nom, aucun modèle . Voir aussi Différer l'exécution pour les chaînes de modèles
ES6

8
Pourriez-vous s'il vous plaît rendre votre message un peu moins élogieux? De plus, il semble que vous aviez l'intention d'écrire un didacticiel au format Q&R, si vous l'avez fait, supprimez la partie « Je vous donne… » de votre question et postez-la comme réponse .
Bergi

Je remarque qu'il y a beaucoup de bonnes réponses ici. Peut-être en accepter un.
abalter

Réponses:


86

Pour que ces littéraux fonctionnent comme les autres moteurs de modèles, il doit y avoir un formulaire intermédiaire.

La meilleure façon de procéder est d'utiliser le Functionconstructeur.

const templateString = "Hello ${this.name}!";
const templateVars = {
    name: "world"    
}

const fillTemplate = function(templateString, templateVars){
    return new Function("return `"+templateString +"`;").call(templateVars);
}

console.log(fillTemplate(templateString, templateVars));

Comme avec d'autres moteurs de modèles, vous pouvez obtenir cette chaîne à partir d'autres endroits, comme un fichier.

Il peut y avoir des problèmes en utilisant cette méthode, comme les balises de modèle sont difficiles à utiliser, mais celles-ci peuvent être ajoutées si vous êtes intelligent. Vous ne pouvez pas non plus avoir de logique JavaScript en ligne en raison de l'interpolation tardive. Cela peut également être corrigé avec une certaine réflexion.


8
Agréable! Vous pouvez même utilisernew Function(`return \`${template}\`;`)
Ruben Stolk

Et ces modèles peuvent être composés ou "inclus" via des arguments en appelant une méthode ou en passant le résultat compilé d'un autre modèle.
Quentin Engles

Quentin Que signifie «pas de balises de modèle»? Merci!
mikemaccana

10
méfiez - vous que cette chaîne de modèle est un peu «cachée» à la transpilation (ie webpack) et ne sera donc PAS transpilée en quelque chose de suffisamment compatible (ie IE11) côté client ...!
Frank Nocke

9
Vulnérabilité XSS ? Détails dans CE JSFIDDLE
Kamil Kiełczewski

65

Vous pouvez mettre une chaîne de modèle dans une fonction:

function reusable(a, b) {
  return `a is ${a} and b is ${b}`;
}

Vous pouvez faire la même chose avec un modèle balisé:

function reusable(strings) {
  return function(... vals) {
    return strings.map(function(s, i) {
      return `${s}${vals[i] || ""}`;
    }).join("");
  };
}

var tagged = reusable`a is ${0} and b is ${1}`; // dummy "parameters"
console.log(tagged("hello", "world"));
// prints "a is hello b is world"
console.log(tagged("mars", "jupiter"));
// prints "a is mars b is jupiter"

L'idée est de laisser l'analyseur de modèle séparer les chaînes constantes de la variable "slots", puis de renvoyer une fonction qui corrige le tout en fonction d'un nouvel ensemble de valeurs à chaque fois.


3
@FelixKling qui peut être; Je vais vérifier et réparer si c'est le cas. edit oui on dirait que j'ai omis une partie importante de l'exemple, qui est la fonction "réutilisable" :)
Pointy

@FelixKling Je ne sais pas trop quoi faire car je ne me souviens pas du tout de ce que je pensais à l'époque!
Pointy

1
Ouais, cela n'a pas beaucoup de sens d'être soit tbh;) Vous pouvez toujours le supprimer .... mais reusablepourrait être implémenté pour qu'il renvoie une fonction, et vous utiliseriez ${0}et ${1}à l'intérieur du littéral au lieu de ${a}et ${b}. Ensuite, vous pouvez utiliser ces valeurs pour faire référence aux arguments de la fonction, similaire à ce que fait Bergi dans son dernier exemple: stackoverflow.com/a/22619256/218196 (ou je suppose que c'est fondamentalement la même chose).
Felix Kling

1
@FelixKling OK, je pense que j'ai trouvé quelque chose qui va au moins vaguement dans le sens de l'OP.
Pointy

3
Les modèles balisés peuvent être très puissants si le résultat n'est en fait pas une chaîne. Par exemple dans l'un de mes projets, je l'utilise pour faire une interpolation de nœuds AST. Par exemple, on peut faire expression`a + ${node}`pour construire un nœud BinaryExpression avec un nœud AST existant node. En interne, nous insérons un espace réservé pour générer du code valide, l'analysons comme un AST et remplaçons l'espace réservé par la valeur transmise.
Felix Kling

45

Le moyen le plus propre de le faire est probablement d'utiliser les fonctions fléchées (car à ce stade, nous utilisons déjà ES6)

var reusable = () => `This ${object} was created by ${creator}`;

var object = "template string", creator = "a function";
console.log (reusable()); // "This template string was created by a function"

object = "example", creator = "me";
console.log (reusable()); // "This example was created by me"

... Et pour les littéraux de gabarit balisés:

reusable = () => myTag`The ${noun} go ${verb} and `;

var noun = "wheels on the bus", verb = "round";
var myTag = function (strings, noun, verb) {
    return strings[0] + noun + strings[1] + verb + strings[2] + verb;
};
console.log (reusable()); // "The wheels on the bus go round and round"

noun = "racecars", verb = "fast";
myTag = function (strings, noun, verb) {
    return strings[0] + noun + strings[1] + verb;
};
console.log (reusable()); // "The racecars go fast"

Cela évite également l'utilisation de eval()ou Function()qui peut causer des problèmes avec les compilateurs et causer beaucoup de ralentissements.


Je pense que c'est l'un des meilleurs car vous pouvez injecter des codes dans la fonction myTagpour faire des choses. Par exemple, utilisez les paramètres d'entrée comme clé pour mettre en cache la sortie.
WOW le

Je pense que c'est la meilleure réponse. Vous pouvez également ajouter des paramètres à la fonction de flèche que je pense en fait encore plus propre: var reusable = (value: string) => `Value is ${value}`.
haggisandchips

13

Réponse de 2019 :

Remarque : la bibliothèque s'attendait à l'origine à ce que les utilisateurs nettoient les chaînes pour éviter XSS. La version 2 de la bibliothèque ne nécessite plus de nettoyer les chaînes utilisateur (ce que les développeurs Web devraient faire de toute façon) car cela évite evalcomplètement.

Le es6-dynamic-templatemodule sur npm le fait.

const fillTemplate = require('es6-dynamic-template');

Contrairement aux réponses actuelles:

  • Il utilise des chaînes de modèle ES6, pas un format similaire. La mise à jour version 2 utilise un format similaire, plutôt que des chaînes de modèle ES6, pour empêcher les utilisateurs d'utiliser des chaînes d'entrée non contrôlées.
  • Il n'est pas nécessaire thisdans la chaîne de modèle
  • Vous pouvez spécifier la chaîne de modèle et les variables dans une seule fonction
  • C'est un module maintenu et actualisable, plutôt que copypasta de StackOverflow

L'utilisation est simple. Utilisez des guillemets simples car la chaîne de modèle sera résolue plus tard!

const greeting = fillTemplate('Hi ${firstName}', {firstName: 'Joe'});

Si vous l'utilisez avec React Native, il se cassera spécialement sur Android. Runtime noeud Android ne supporte pas les modèles dynamiques, SEULS préremplis
Oliver Dixon

1
C'est une solution que j'utilise dans mes projets personnels et qui fonctionne parfaitement. En fait, je pense que c'est une mauvaise idée d'utiliser trop de bibliothèques, en particulier pour de petits utilitaires comme celui-ci.
Oliver Dixon

1
Vulnérabilité XSS ? Détails dans CE FIDDLE
Kamil Kiełczewski

1
@kamil Uniquement XSS si vous a) donnez aux utilisateurs la possibilité de créer b) ne nettoyez pas les chaînes d'entrée. J'ajouterai un avertissement selon lequel les gens devraient nettoyer les entrées des utilisateurs.
mikemaccana

1
Cela n'utilise pas à distance les littéraux de modèle es6. Essayez 10 * 20 = ${10 * 20}donc cela pourrait être un format similaire, mais ce n'est même pas à distance les littéraux de modèle
es6

12

Oui, vous pouvez le faire en analysant votre chaîne avec le modèle en tant que JS par Function(ou eval) - mais ce n'est pas recommandé et autorise l' attaque XSS

Au lieu de cela, vous pouvez insérer en toute sécurité desobj champs d' objet dans le modèle strde manière dynamique comme suit

let inject = (str, obj) => str.replace(/\${(.*?)}/g, (x,g)=> obj[g]);


C'est la méthode que j'utilise et ça marche bien. Bon exemple! Est-ce que le? après le * dans l'aide RegEx? Je ne suis pas un expert RegEx, mais je suppose que puisque le * signifie zéro ou plus (et vous voulez le "plus" dans ce cas), il n'y a pas besoin de la restriction gourmande?
Gen1-1

@ Gen1-1 le .*?moyen non gourmand - si vous supprimez, l' "?"extrait donnera un résultat erroné
Kamil Kiełczewski

Vous avez raison, mon erreur. Je n'utilise pas $ dans mes modèles et j'utilise RegEx: / {(\ w *)} / g car je n'ai jamais d'espace dans la balise, mais. *? fonctionne également. J'utilise:function 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 || ""; }); }
Gen1-1

@ Gen1-1 existe-t-il également des données imbriquées possibles? comme data = { a: 1, b: { c:2, d:3 } }-> b.c?
muescha le

1
@muescha Vous changeriez la ligne: value = data [clé], pour utiliser la récursivité et rechercher votre objet de données entier et les objets imbriqués jusqu'à ce que vous trouviez la propriété. Exemples: codereview.stackexchange.com/questions/73714/… , et mikedoesweb.com/2016/es6-depth-first-object-tree-search
Gen1-1

9

Simplifier la réponse fournie par @metamorphasi;

const fillTemplate = function(templateString, templateVars){
  var func = new Function(...Object.keys(templateVars),  "return `"+templateString +"`;")
  return func(...Object.values(templateVars));
}

// Sample
var hosting = "overview/id/d:${Id}";
var domain = {Id:1234, User:22};
var result = fillTemplate(hosting, domain);

console.log(result);


Ce code est plus explicite que la réponse principale. Got my up-vote :)
ymz

Cela devrait vous permettre d'utiliser des variables ou des fichiers externes (dans NodeJS) comme modèles ou de les créer dynamiquement au moment de l'exécution. Sans l'utilisation de eval.
b01 le

Vulnérabilité XSS ? Fiddle avec code malveillant (variable var hosting) ICI .
Kamil Kiełczewski

7

Si vous ne souhaitez pas utiliser les paramètres commandés ou le contexte / namespaces pour référencer les variables dans votre modèle, par exemple ${0}, ${this.something}ou ${data.something}, vous pouvez avoir une fonction de modèle qui prend soin de la portée pour vous.

Exemple de la façon dont vous pourriez appeler un tel modèle:

const tempGreet = Template(() => `
  <span>Hello, ${name}!</span>
`);
tempGreet({name: 'Brian'}); // returns "<span>Hello, Brian!</span>"

La fonction Template:

function Template(cb) {
  return function(data) {
    const dataKeys = [];
    const dataVals = [];
    for (let key in data) {
      dataKeys.push(key);
      dataVals.push(data[key]);
    }
    let func = new Function(...dataKeys, 'return (' + cb + ')();');
    return func(...dataVals);
  }
}

La bizarrerie dans ce cas est qu'il vous suffit de passer une fonction (dans l'exemple, j'ai utilisé une fonction de flèche) qui renvoie le littéral du modèle ES6. Je pense que c'est un compromis mineur pour obtenir le type d'interpolation réutilisable que nous recherchons.

Le voici sur GitHub: https://github.com/Adelphos/ES6-Reuseable-Template


3
C'est bien, mais la minification (vals, func, etc.) est inutile, 'cb' n'est pas un rappel (c'est entièrement du code de synchronisation), et vous pouvez simplement utiliser Object.values()etObject.keys()
mikemaccana

3

La réponse courte est simplement d'utiliser _.template dans lodash

// Use the ES template literal delimiter as an "interpolate" delimiter.
// Disable support by replacing the "interpolate" delimiter.
var compiled = _.template('hello ${ user }!');
compiled({ 'user': 'pebbles' });
// => 'hello pebbles!'

3

Est-ce que je manque quelque chose? Existe-t-il un [bon] moyen de rendre un modèle réutilisable littéral?

Peut - être que je manque quelque chose, car ma solution à ce problème me semble si évidente que je suis très surpris que personne n'ait déjà écrit cela dans une question aussi ancienne.

J'ai presque une seule ligne pour cela:

function defer([first, ...rest]) {
  return (...values) => rest.reduce((acc, str, i) => acc + values[i] + str, first);
}

C'est tout. Lorsque je veux réutiliser un modèle et reporter la résolution des substitutions, je fais simplement:

> t = defer`My template is: ${null} and ${null}`;
> t('simple', 'reusable');          // 'My template is: simple and reusable'
> t('obvious', 'late to the party'; // 'My template is: obvious and late to the party'
> t(null);                          // 'My template is: null and undefined'
>
> defer`Choose: ${'ignore'} / ${undefined}`(true, false); // 'Choose: true / false'

L'application de cette balise renvoie un 'function'(au lieu d'un 'string') qui ignore tous les paramètres passés au littéral. Ensuite, il peut être appelé avec de nouveaux paramètres plus tard. Si un paramètre n'a pas de remplacement correspondant, il devient 'undefined'.


Réponse étendue

Ce code simple est fonctionnel, mais si vous avez besoin d'un comportement plus élaboré, cette même logique peut être appliquée et les possibilités sont infinies. Vous pourriez:

  1. Utilisez les paramètres d'origine:

Vous pouvez stocker les valeurs d'origine transmises au littéral dans la construction et les utiliser de manière créative lors de l'application du modèle. Ils peuvent devenir des indicateurs, des validateurs de type, des fonctions, etc. Voici un exemple qui les utilise comme valeurs par défaut:

    function deferWithDefaults([first, ...rest], ...defaults) {
      return (...values) => rest.reduce((acc, curr, i) => {
        return acc + (i < values.length ? values[i] : defaults[i]) + curr;
      }, first);
    }

Ensuite:

    > t = deferWithDefaults`My template is: ${'extendable'} and ${'versatile'}`;
    > t('awesome');                 // 'My template is: awesome and versatile' 
  1. Ecrire une fabrique de modèles:

Faites-le en enveloppant cette logique dans une fonction qui attend, comme argument, une fonction personnalisée qui peut être appliquée dans la réduction (lors de la jonction des éléments du littéral de modèle) et retourne un nouveau modèle avec un comportement personnalisé.

    const createTemplate = fn => function (strings, ...defaults) {
      const [first, ...rest] = strings;
      return (...values) => rest.reduce((acc, curr, i) => {
        return acc + fn(values[i], defaults[i]) + curr;
      }, first);
    };

Ensuite, vous pouvez, par exemple, écrire des modèles qui échappent ou nettoient automatiquement les paramètres lors de l'écriture html, css, sql, bash ...

    function sqlSanitize(token, tag) {
      // this is a gross simplification, don't use in production.
      const quoteName = name => (!/^[a-z_][a-z0-9_$]*$/.test(name) ? `"${name.replace(/"/g, '""')}"` : name);
      const quoteValue = value => (typeof value == 'string' ? `'${value.replace(/'/g, "''")}'` : value);
      switch (tag) {
        case 'table':
          return quoteName(token);
        case 'columns':
          return token.map(quoteName);
        case 'row':
          return token.map(quoteValue);
        default:
          return token;
      }
    }

    const sql = createTemplate(sqlSanitize);

Avec ce modèle SQL naïf (je le répète, naïf! ), Nous pourrions créer des requêtes comme celle-ci:

    > q  = sql`INSERT INTO ${'table'} (${'columns'})
    ... VALUES (${'row'});`
    > q('user', ['id', 'user name', 'is"Staff"?'], [1, "O'neil", true])
    // `INSERT INTO user (id,"user name","is""Staff""?")
    // VALUES (1,'O''neil',true);`
  1. Acceptez les paramètres nommés pour la substitution: un exercice pas si difficile, basé sur ce qui a déjà été donné. Il y a une implémentation dans cette autre réponse .

  2. Faites en sorte que l'objet de retour se comporte comme un 'string': Eh bien, c'est controversé, mais pourrait conduire à des résultats intéressants. Montré dans cette autre réponse .

  3. Résolvez les paramètres dans l'espace de noms global sur le site d'appel:

Je vous donne, des littéraux de modèle réutilisables:

Eh bien, voici ce OP est montré son additif, en utilisant la commande evil, je veux dire, eval. Cela pourrait être fait sans eval, simplement en recherchant le nom de variable passé dans l'objet global (ou window). Je ne montrerai pas comment le faire car je ne l'aime pas. Les fermetures sont le bon choix.


2

C'est ma meilleure tentative:

var s = (item, price) => {return `item: ${item}, price: $${price}`}
s('pants', 10) // 'item: pants, price: $10'
s('shirts', 15) // 'item: shirts, price: $15'

Pour généraliser:

var s = (<variable names you want>) => {return `<template with those variables>`}

Si vous n'exécutez pas E6, vous pouvez également faire:

var s = function(<variable names you want>){return `<template with those variables>`}

Cela semble être un peu plus concis que les réponses précédentes.

https://repl.it/@abalter/reusable-JS-template-literal


2

En général, je suis contre l'utilisation du mal eval(), mais dans ce cas, cela a du sens:

var template = "`${a}.${b}`";
var a = 1, b = 2;
var populated = eval(template);

console.log(populated);         // shows 1.2

Ensuite, si vous modifiez les valeurs et appelez à nouveau eval (), vous obtenez le nouveau résultat:

a = 3; b = 4;
populated = eval(template);

console.log(populated);         // shows 3.4

Si vous le voulez dans une fonction, alors il peut être écrit comme ceci:

function populate(a, b){
  return `${a}.${b}`;
}

Si vous écrivez une fonction qui inclut le modèle, vous ne devriez certainement pas utiliser eval.
Bergi

@Bergi Pourquoi? En quoi est-ce différent de votre implémentation?
isapir le

2
Les raisons que je "semble connaître" s'appliquent à tout code construit dynamiquement. Ecrire la fonction de manière à générer le résultat sans appeler eval()explicitement est exactement la même chose que eval(), par conséquent, cela ne présente aucun avantage car cela ne fait que rendre le code plus difficile à lire.
isapir le

1
Exactement. Et puisque votre populatefonction ne construit pas dynamiquement le code, elle ne doit pas l'utiliser evalavec tous ses inconvénients.
Bergi

6
votre fonction pourrait simplement être function populate(a,b) { return `${a}.${b}`; }l'évaluation n'ajoute rien
Vitim.us

1

MISE À JOUR: La réponse suivante est limitée aux noms de variables uniques, donc, les modèles tels que: 'Result ${a+b}'ne sont pas valides pour ce cas. Cependant, vous pouvez toujours jouer avec les valeurs du modèle:

format("This is a test: ${a_b}", {a_b: a+b});

RÉPONSE ORIGINALE:

Basé sur les réponses précédentes mais créant une fonction utilitaire plus "conviviale":

var format = (template, params) => {
    let tpl = template.replace(/\${(?!this\.)/g, "${this.");
    let tpl_func = new Function(`return \`${tpl}\``);

    return tpl_func.call(params);
}

Vous pouvez l'invoquer comme:

format("This is a test: ${hola}, second param: ${hello}", {hola: 'Hola', hello: 'Hi'});

Et la chaîne résultante doit être:

'This is a test: Hola, second param: Hi'

Et un modèle comme celui-ci? `Result: ${a+b}`
Atiris

1
Salut @Atiris, tu as raison, c'est une limitation, j'ai mis à jour ma réponse.
Roberto

1

Si vous cherchez quelque chose d'assez simple (juste des champs variables fixes, pas de calculs, de conditionnels…) mais qui fonctionne aussi côté client sur les navigateurs sans support de chaîne de modèle comme IE 8,9,10,11

Et c'est parti:

fillTemplate = function (templateString, templateVars) {
    var parsed = templateString;
    Object.keys(templateVars).forEach(
        (key) => {
            const value = templateVars[key]
            parsed = parsed.replace('${'+key+'}',value)
        }
    )
    return parsed
}

Cela fera une recherche pour chaque variable. Il existe un autre moyen qui remplace toutes les occurrences à la fois que j'ai implémentées dans ce module: safe-es6-template
Aalex Gabi

1

Je suis ennuyé de la redondance supplémentaire nécessaire de taper à this.chaque fois, donc j'ai aussi ajouté regex pour développer des variables comme .aà this.a.

Solution:

const interp = template => _thisObj =>
function() {
    return template.replace(/\${([^}]*)}/g, (_, k) =>
        eval(
            k.replace(/([.a-zA-Z0-9$_]*)([a-zA-Z0-9$_]+)/, (r, ...args) =>
                args[0].charAt(0) == '.' ? 'this' + args[0] + args[1] : r
            )
        )
    );
}.call(_thisObj);

Utilisez comme tel:

console.log(interp('Hello ${.a}${.b}')({ a: 'World', b: '!' }));
// outputs: Hello World!

1

Je viens de publier un package npm qui peut simplement faire ce travail. Profondément inspiré par cette réponse .

const Template = require('dynamic-template-string');

var tpl = new Template('hello ${name}');

tpl.fill({name: 'world'}); // ==> 'hello world';
tpl.fill({name: 'china'}); // ==> 'hello china';

Son outil est d'une simplicité mortelle. Je souhaite que vous l'aimiez.


module.exports = class Template {
  constructor(str) {
    this._func = new Function(`with(this) { return \`${str}\`; }`);
  }

  fill(data) {
    return this._func.call(data);
  }
}

1

vous pouvez utiliser la fonction de flèche en ligne comme celle-ci, définition:

const template = (substitute: string) => `[^.?!]*(?<=[.?\s!])${substitute}(?=[\s.?!])[^.?!]*[.?!]`;

usage:

console.log(template('my replaced string'));

1

Chaîne de modèle d'exécution

var templateString = (template, values) => {
    let output = template;
    Object.keys(values)
        .forEach(key => {
        output = output.replace(new RegExp('\\$' + `{${key}}`, 'g'), values[key]);
    });
    return output;
};

Tester

console.debug(templateString('hello ${word} world', {word: 'wonderful'}));

0

const fillTemplate = (template, values) => {
  template = template.replace(/(?<=\${)\w+(?=})/g, v=>"this."+v);
  return Function.apply(this, ["", "return `"+template+"`;"]).call(values);
};

console.log(fillTemplate("The man ${man} is brother of ${brother}", {man: "John", brother:"Peter"}));
//The man John is brother of Peter

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.