Convertir une chaîne en chaîne de modèle

147

Est-il possible de créer une chaîne de modèle comme une chaîne habituelle

let a="b:${b}";

puis le convertir en chaîne de modèle

let b=10;
console.log(a.template());//b:10

sans eval, new Functionet d'autres moyens de génération de code dynamique?

KOLANICH
la source
5
avez-vous trouvé un moyen d'y parvenir? J'aurai peut-être besoin de le faire un jour et je suis curieux de savoir à quoi vous êtes arrivé.
Bryan Rayner
@BryanRayner disons que votre programme js essaie de récupérer une donnée de l'API rest, dont l'url est dans un fichier config.js sous forme de chaîne "/ resources / <resource_id> / update /" et que vous mettez "resource_id" dynamiquement à partir de votre programme . À moins que vous ne souhaitiez diviser cette URL en parties et enregistrer dans différentes zones, vous avez besoin d'une sorte de traitement de modèle de chaîne.
Ryu_hayabusa
Au lieu d'utiliser eval, mieux vaut utiliser regex Eval, ce n'est pas recommandé et fortement déconseillé, donc ne l'utilisez pas developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…! soit b = 10; laissez a = "b: $ {b}"; let response = a.replace (/ \ $ {\ w +} /, b); conssole.log (réponse);
Vijay Palaskar le

Réponses:

79

Comme votre chaîne de modèle doit obtenir une référence à la bvariable de manière dynamique (au moment de l'exécution), la réponse est: NON, il est impossible de se passer de la génération de code dynamique.

Mais avec evalc'est assez simple:

let tpl = eval('`'+a+'`');
alexpods
la source
7
eval n'est pas sécurisé, tout comme les autres moyens de génération de code dynamique
KOLANICH
8
@KOLANICH Pour ce cas particulier - citations de retour d'échappement dans la achaîne et il sera beaucoup moins précaire: let tpl = eval('`'+a.replace(/`/g,'\\`')+'`');. Je pense que le plus important est d' evalempêcher le compilateur d'optimiser votre code. Mais je pense que cela n'a aucun rapport avec cette question.
alexpods
3
En fait, vous pouvez également exécuter des fonctions dans des chaînes de modèle.
KOLANICH
9
@KOLANICH Désolé, vous n'aimez pas eval. Cependant, rappelez-vous qu'un modèle littéral est lui-même une forme de eval. Deux exemples: var test = Result: ${alert('hello')}; test var = Result: ${b=4}; Les deux finiront par exécuter du code arbitraire dans le contexte du script. Si vous souhaitez autoriser des chaînes arbitraires, vous pouvez aussi bien autoriser eval.
Manngo
6
Faites attention. Comme quelque chose comme babel ne transpilera pas cela, ce code ne fonctionnera PAS dans IE
cgsd
79

Dans mon projet, j'ai créé quelque chose comme ça avec ES6:

String.prototype.interpolate = function(params) {
  const names = Object.keys(params);
  const vals = Object.values(params);
  return new Function(...names, `return \`${this}\`;`)(...vals);
}

const template = 'Example text: ${text}';
const result = template.interpolate({
  text: 'Foo Boo'
});
console.log(result);

MISE À JOUR J'ai supprimé la dépendance de lodash, ES6 a des méthodes équivalentes pour obtenir des clés et des valeurs.

Mateusz Moska
la source
1
Salut, Votre solution fonctionne très bien, mais lorsque je l'ai utilisée dans React Native (mode de construction), elle génère une erreur: Caractère invalide `` '' , bien que cela fonctionne lorsque je l'exécute en mode débogage. On dirait, problème babel, une aide?
Mohit Pandey
@MohitPandey J'obtenais la même erreur lorsque j'exécutais des tests de ce code sous PhantomJS et qu'il passait sous chrome. Si tel est le cas, je pense qu'une nouvelle version bêta de PhantomJS est en route avec un meilleur support pour ES6, vous pouvez essayer de l'installer.
Mateusz Moska
1
Malheureusement, cela ne fonctionne pas et j'ai écrit une regex pour la même chose. Ajouté comme réponse également.
Mohit Pandey
cette solution ne fonctionne que si le caractère back-tick "` "n'est pas présent dans la chaîne de modèle
SliverNinja - MSFT
Quand je l'essaie, je l'ai ReferenceError: _ is not defined. N'est-ce pas du code ES6 mais lodashspécifique, ou ...?
xpt
29

Ce que vous demandez ici:

//non working code quoted from the question
let b=10;
console.log(a.template());//b:10

est exactement équivalent (en termes de puissance et, euh, de sécurité) à eval : la capacité de prendre une chaîne contenant du code et d'exécuter ce code; et aussi la possibilité pour le code exécuté de voir les variables locales dans l'environnement de l'appelant.

Il n'y a aucun moyen dans JS pour une fonction de voir les variables locales dans son appelant, à moins que cette fonction ne le soit eval(). Function()Je ne peux même pas le faire.


Lorsque vous entendez qu'il y a quelque chose appelé "chaînes de modèles" venant de JavaScript, il est naturel de supposer qu'il s'agit d'une bibliothèque de modèles intégrée, comme Moustache. Ça ne l'est pas. Il s'agit principalement d'une interpolation de chaînes et de chaînes multilignes pour JS. Je pense cependant que ce sera une idée fausse courante pendant un certain temps. :(

Jason Orendorff
la source
2
TBH c'est ce que je pensais que c'était. Cela aurait été très, très pratique.
Bryan Rayner
Est-ce que cela fonctionne (toujours)? Je reçois template is not a function.
Ionică Bizău
2
Le bloc de code en haut de cette réponse est une citation de la question. Ça ne marche pas.
Jason Orendorff
27

Non, il n'y a pas moyen de faire cela sans génération de code dynamique.

Cependant, j'ai créé une fonction qui transformera une chaîne régulière en une fonction qui peut être fournie avec une carte de valeurs, en utilisant des chaînes de modèle en interne.

Générer la liste de chaînes de modèle

/**
 * Produces a function which uses template strings to do simple interpolation from objects.
 * 
 * Usage:
 *    var makeMeKing = generateTemplateString('${name} is now the king of ${country}!');
 * 
 *    console.log(makeMeKing({ name: 'Bryan', country: 'Scotland'}));
 *    // Logs 'Bryan is now the king of Scotland!'
 */
var generateTemplateString = (function(){
    var cache = {};

    function generateTemplate(template){
        var fn = cache[template];

        if (!fn){
            // Replace ${expressions} (etc) with ${map.expressions}.

            var sanitized = template
                .replace(/\$\{([\s]*[^;\s\{]+[\s]*)\}/g, function(_, match){
                    return `\$\{map.${match.trim()}\}`;
                    })
                // Afterwards, replace anything that's not ${map.expressions}' (etc) with a blank string.
                .replace(/(\$\{(?!map\.)[^}]+\})/g, '');

            fn = Function('map', `return \`${sanitized}\``);
        }

        return fn;
    }

    return generateTemplate;
})();

Usage:

var kingMaker = generateTemplateString('${name} is king!');

console.log(kingMaker({name: 'Bryan'}));
// Logs 'Bryan is king!' to the console.

J'espère que cela aide quelqu'un. Si vous rencontrez un problème avec le code, veuillez mettre à jour le Gist.

Bryan Rayner
la source
Merci! J'ai utilisé ceci au lieu d'une solution javascript sprintf.
seangwright
1
ne fonctionne pas pour chaque var test = generateTemplateString('/api/${param1}/${param2}/') console.log(test({param1: 'bar', param2: 'foo'}))retour de modèles/api/bar//
Guillaume Vincent
Merci, corrigé. Le regex incluait une seule correspondance de $ {param1} / $ {param2} alors qu'il aurait dû y avoir deux correspondances.
Bryan Rayner le
Notez que celui-ci ne fonctionne pas dans IE11, en raison du manque de prise en charge des contre-graduations.
s.meijer
1
Bien sûr, si les chaînes de modèle ne sont pas prises en charge par un navigateur, cette méthode ne fonctionnera pas. Si vous souhaitez utiliser des chaînes de modèle dans des navigateurs non pris en charge, je vous recommande d'utiliser un langage comme TypeScript ou un transpilateur comme Babel; C'est la seule façon d'intégrer ES6 dans les anciens navigateurs.
Bryan Rayner
9

TLDR: https://jsfiddle.net/w3jx07vt/

Tout le monde semble s'inquiéter d'accéder aux variables, pourquoi ne pas simplement les transmettre? Je suis sûr qu'il ne sera pas trop difficile d'obtenir le contexte variable dans l'appelant et de le transmettre. Utilisez ce https://stackoverflow.com/a/6394168/6563504 pour obtenir les accessoires de obj. Je ne peux pas tester pour vous pour le moment, mais cela devrait fonctionner.

function renderString(str,obj){
    return str.replace(/\$\{(.+?)\}/g,(match,p1)=>{return index(obj,p1)})
}

Testé. Voici le code complet.

function index(obj,is,value) {
    if (typeof is == 'string')
        is=is.split('.');
    if (is.length==1 && value!==undefined)
        return obj[is[0]] = value;
    else if (is.length==0)
        return obj;
    else
        return index(obj[is[0]],is.slice(1), value);
}

function renderString(str,obj){
    return str.replace(/\$\{.+?\}/g,(match)=>{return index(obj,match)})
}

renderString('abc${a}asdas',{a:23,b:44}) //abc23asdas
renderString('abc${a.c}asdas',{a:{c:22,d:55},b:44}) //abc22asdas
M3D
la source
@ s.meijer pourriez-vous élaborer? J'utilise ce code avec succès. jsfiddle.net/w3jx07vt
M3D
1
Une meilleure regex vous permettrait de ne pas sélectionner les ${}caractères. Essayez:/(?!\${)([^{}]*)(?=})/g
Eric Hodonsky
@Relic jsfiddle.net/w3jx07vt/2 Je n'ai pas réussi à faire fonctionner cela, vous voulez donner un coup de main et je mettrai à jour mon message? :)
M3D
Donc, la façon dont vous essayez de le saisir n'aidera pas vraiment beaucoup, j'ai fini par faire un remplacement de chaîne à la place. Au lieu d'ajouter une étape d'interpolation, je peux donc utiliser la chaîne comme interp ou chaîne. Pas de fantaisie, mais cela a fonctionné.
Eric Hodonsky
8

Le problème ici est d'avoir une fonction qui a accès aux variables de son appelant. C'est pourquoi nous voyons l'utilisation directe evalpour le traitement des modèles. Une solution possible serait de générer une fonction prenant des paramètres formels nommés par les propriétés d'un dictionnaire, et l'appelant avec les valeurs correspondantes dans le même ordre. Une autre manière serait d'avoir quelque chose de simple comme ceci:

var name = "John Smith";
var message = "Hello, my name is ${name}";
console.log(new Function('return `' + message + '`;')());

Et pour quiconque utilise le compilateur Babel, nous devons créer une fermeture qui se souvient de l'environnement dans lequel il a été créé:

console.log(new Function('name', 'return `' + message + '`;')(name));
didinko
la source
Votre premier extrait de code est en fait pire que evalparce qu'il ne fonctionne qu'avec une namevariable globale
Bergi
@Bergi Votre déclaration est valide - la portée de la fonction sera perdue. Je voulais présenter une solution simple au problème et fournir un exemple simplifié de ce qui peut être fait. On pourrait simplement trouver ce qui suit pour surmonter le problème: var template = function() { var name = "John Smith"; var message = "Hello, my name is ${name}"; this.local = new Function('return '+ message +';')();}
didinko
Non, c'est exactement ce qui ne fonctionnerait pas - new Functionn'a pas accès à var namela templatefonction.
Bergi
Le deuxième snip a résolu mon problème ... Votez de ma part! Merci, cela a aidé à résoudre un problème temporaire que nous avions avec le routage dynamique vers une iframe :)
Kris Boyd
7

Il existe de nombreuses bonnes solutions publiées ici, mais aucune n'utilise encore la méthode ES6 String.raw . Voici ma contribution. Il a une limitation importante en ce qu'il n'acceptera que les propriétés d'un objet passé, ce qui signifie qu'aucune exécution de code dans le modèle ne fonctionnera.

function parseStringTemplate(str, obj) {
    let parts = str.split(/\$\{(?!\d)[\wæøåÆØÅ]*\}/);
    let args = str.match(/[^{\}]+(?=})/g) || [];
    let parameters = args.map(argument => obj[argument] || (obj[argument] === undefined ? "" : obj[argument]));
    return String.raw({ raw: parts }, ...parameters);
}
let template = "Hello, ${name}! Are you ${age} years old?";
let values = { name: "John Doe", age: 18 };

parseStringTemplate(template, values);
// output: Hello, John Doe! Are you 18 years old?
  1. Divisez la chaîne en parties textuelles sans argument. Voir regex .
    parts: ["Hello, ", "! Are you ", " years old?"]
  2. Divisez la chaîne en noms de propriétés. Tableau vide si la correspondance échoue.
    args: ["name", "age"]
  3. Mappez les paramètres objpar nom de propriété. La solution est limitée par une cartographie à un niveau peu profonde. Les valeurs non définies sont remplacées par une chaîne vide, mais d'autres valeurs erronées sont acceptées.
    parameters: ["John Doe", 18]
  4. Utiliser String.raw(...)et renvoyer le résultat.
pekaaw
la source
Par curiosité, quelle valeur fournit String.raw ici? Il semble que vous faites tout le travail d'analyse de la chaîne et de suivi de ce que sont les sous-stations. Est-ce bien différent de simplement appeler à .replace()plusieurs reprises?
Steve Bennett
Bon point, @SteveBennett. J'ai eu quelques problèmes pour transformer une chaîne normale en chaîne de modèle et j'ai trouvé une solution en créant moi-même l'objet brut. Je suppose que cela réduit String.raw à une méthode de concaténation, mais je pense que cela fonctionne assez bien. J'aimerais voir une bonne solution avec .replace(), cependant :) Je pense que la lisibilité est importante, donc tout en utilisant moi-même des expressions régulières, j'essaie de les nommer pour aider à comprendre tout cela ...
pekaaw
6

Similaire à la réponse de Daniel (et à l' essentiel de mon collègue ) mais plus lisible:

const regex = /\${[^{]+}/g;

export default function interpolate(template, variables, fallback) {
    return template.replace(regex, (match) => {
        const path = match.slice(2, -1).trim();
        return getObjPath(path, variables, fallback);
    });
}

//get the specified property or nested property of an object
function getObjPath(path, obj, fallback = '') {
    return path.split('.').reduce((res, key) => res[key] || fallback, obj);
}

Remarque: Cela améliore légèrement l'original de s.meijer, car il ne correspondra pas à des choses comme ${foo{bar}(le regex autorise uniquement les caractères non bouclés à l'intérieur ${et }).


MISE À JOUR: On m'a demandé un exemple d'utilisation de ceci, alors voilà:

const replacements = {
    name: 'Bob',
    age: 37
}

interpolate('My name is ${name}, and I am ${age}.', replacements)
Matt Browne
la source
Pouvez-vous poster un exemple utilisant réellement ceci? Ce javascript me dépasse un peu. Je suggérerais une expression régulière de /\$\{(.*?)(?!\$\{)\}/g(pour gérer les accolades nidifiées). J'ai une solution qui fonctionne mais je ne suis pas sûr qu'elle soit aussi portable que la vôtre, alors j'aimerais voir comment cela devrait être implémenté dans une page. Le mien utilise également eval().
Regular Joe
J'ai également publié une réponse et j'aimerais avoir vos commentaires sur la façon de rendre cela plus sûr et plus performant: stackoverflow.com/a/48294208 .
Regular Joe
@RegularJoe J'ai ajouté un exemple. Mon objectif était de rester simple, mais vous avez raison de dire que si vous voulez gérer des accolades imbriquées, vous devrez changer l'expression régulière. Cependant, je ne peux pas penser à un cas d'utilisation pour cela lors de l'évaluation d'une chaîne régulière comme s'il s'agissait d'un littéral de modèle (tout le but de cette fonction). Qu'avais tu en tête?
Matt Browne
De plus, je ne suis ni un expert en performance ni un expert en sécurité; ma réponse ne fait que combiner deux réponses précédentes. Mais je dirai que l'utilisation evalvous laisse beaucoup plus ouvert à d'éventuelles erreurs qui pourraient causer des problèmes de sécurité, alors que ma version ne fait que rechercher une propriété sur un objet à partir d'un chemin séparé par des points, ce qui devrait être sûr.
Matt Browne
5

Vous pouvez utiliser le prototype de chaîne, par exemple

String.prototype.toTemplate=function(){
    return eval('`'+this+'`');
}
//...
var a="b:${b}";
var b=10;
console.log(a.toTemplate());//b:10

Mais la réponse à la question initiale n'est pas possible.

Sarkiroka
la source
5

J'ai aimé la réponse de mon collègue et j'ai écrit ma propre version basée sur la sienne:

function parseTemplate(template, map, fallback) {
    return template.replace(/\$\{[^}]+\}/g, (match) => 
        match
            .slice(2, -1)
            .trim()
            .split(".")
            .reduce(
                (searchObject, key) => searchObject[key] || fallback || match,
                map
            )
    );
}
Daniel
la source
1
Soigné! Vraiment bien!
xpt
4

J'avais besoin de cette méthode avec le support d'Internet Explorer. Il s'est avéré que les tiques arrière ne sont même pas prises en charge par IE11. Aussi; utiliser evalou son équivalent Functionne semble pas correct.

Pour celui qui remarque; J'utilise également des backticks, mais ceux-ci sont supprimés par des compilateurs comme babel. Les méthodes suggérées par d'autres dépendent d'elles au moment de l'exécution. Comme dit précédemment; il s'agit d'un problème dans IE11 et les versions antérieures.

Voici donc ce que j'ai proposé:

function get(path, obj, fb = `$\{${path}}`) {
  return path.split('.').reduce((res, key) => res[key] || fb, obj);
}

function parseTpl(template, map, fallback) {
  return template.replace(/\$\{.+?}/g, (match) => {
    const path = match.substr(2, match.length - 3).trim();
    return get(path, map, fallback);
  });
}

Exemple de sortie:

const data = { person: { name: 'John', age: 18 } };

parseTpl('Hi ${person.name} (${person.age})', data);
// output: Hi John (18)

parseTpl('Hello ${person.name} from ${person.city}', data);
// output: Hello John from ${person.city}

parseTpl('Hello ${person.name} from ${person.city}', data, '-');
// output: Hello John from -
s.meijer
la source
"Utiliser eval ou son équivalent Function ne semble pas correct." ... Ouais ... Je suis d'accord, mais je pense que c'est l'un des très rares cas d'utilisation dans lesquels on pourrait dire, "mmhkay, utilisons-le". Veuillez consulter jsperf.com/es6-string-tmpl - c'est mon cas d'utilisation pratique. En utilisant votre fonction (avec la même expression rationnelle que la mienne) et la mienne (eval + string literals). Merci! :)
Andrea Puddu
@AndreaPuddu, vos performances sont en effet meilleures. Mais encore une fois; les chaînes de modèle ne sont pas prises en charge dans IE. Cela eval('`' + taggedURL + '`')ne fonctionne tout simplement pas.
s.meijer
"Semble" mieux je dirais, car il est testé isolément ... Le seul but de ce test était de voir les problèmes de performances potentiels en utilisant eval. En ce qui concerne les modèles littéraux: merci de le souligner à nouveau. J'utilise Babel pour transpiler mon code mais ma fonction ne fonctionnera toujours pas apparemment 😐
Andrea Puddu
3

Je ne peux actuellement pas commenter les réponses existantes, donc je ne peux pas commenter directement l'excellente réponse de Bryan Raynor. Ainsi, cette réponse va mettre à jour sa réponse avec une légère correction.

En bref, sa fonction ne parvient pas à mettre en cache la fonction créée, elle sera donc toujours recréée, qu'elle ait ou non vu le modèle auparavant. Voici le code corrigé:

    /**
     * Produces a function which uses template strings to do simple interpolation from objects.
     * 
     * Usage:
     *    var makeMeKing = generateTemplateString('${name} is now the king of ${country}!');
     * 
     *    console.log(makeMeKing({ name: 'Bryan', country: 'Scotland'}));
     *    // Logs 'Bryan is now the king of Scotland!'
     */
    var generateTemplateString = (function(){
        var cache = {};

        function generateTemplate(template){
            var fn = cache[template];

            if (!fn){
                // Replace ${expressions} (etc) with ${map.expressions}.

                var sanitized = template
                    .replace(/\$\{([\s]*[^;\s\{]+[\s]*)\}/g, function(_, match){
                        return `\$\{map.${match.trim()}\}`;
                    })
                    // Afterwards, replace anything that's not ${map.expressions}' (etc) with a blank string.
                    .replace(/(\$\{(?!map\.)[^}]+\})/g, '');

                fn = cache[template] = Function('map', `return \`${sanitized}\``);
            }

            return fn;
        };

        return generateTemplate;
    })();
user2501097
la source
3

@Mateusz Moska, la solution fonctionne très bien, mais quand je l'ai utilisée dans React Native (mode construction), elle génère une erreur: caractère invalide '' ' , bien que cela fonctionne lorsque je l'exécute en mode débogage.

J'ai donc écrit ma propre solution en utilisant regex.

String.prototype.interpolate = function(params) {
  let template = this
  for (let key in params) {
    template = template.replace(new RegExp('\\$\\{' + key + '\\}', 'g'), params[key])
  }
  return template
}

const template = 'Example text: ${text}',
  result = template.interpolate({
    text: 'Foo Boo'
  })

console.log(result)

Démo: https://es6console.com/j31pqx1p/

REMARQUE: comme je ne connais pas la cause profonde d'un problème, j'ai soulevé un ticket dans le repo react-native, https://github.com/facebook/react-native/issues/14107 , afin qu'une fois qu'ils puissent corrige / guide-moi à peu près pareil :)

Mohit Pandey
la source
cela prend en charge les modèles contenant le caractère back-tick. Cependant, plutôt que d'essayer d'inventer un modèle de modèle, vous feriez probablement mieux d' utiliser simplement la moustache ou similaire . selon la complexité de vos modèles, il s'agit d'une approche par force brute qui ne prend pas en compte les cas extrêmes - la clé peut contenir un modèle d'expression régulière spécial.
SliverNinja - MSFT
2

Toujours dynamique mais semble plus contrôlé que la simple utilisation d'une évaluation nue:

const vm = require('vm')
const moment = require('moment')


let template = '### ${context.hours_worked[0].value} \n Hours worked \n #### ${Math.abs(context.hours_worked_avg_diff[0].value)}% ${fns.gt0(context.hours_worked_avg_diff[0].value, "more", "less")} than usual on ${fns.getDOW(new Date())}'
let context = {
  hours_worked:[{value:10}],
  hours_worked_avg_diff:[{value:10}],

}


function getDOW(now) {
  return moment(now).locale('es').format('dddd')
}

function gt0(_in, tVal, fVal) {
  return _in >0 ? tVal: fVal
}



function templateIt(context, template) {
  const script = new vm.Script('`'+template+'`')
  return script.runInNewContext({context, fns:{getDOW, gt0 }})
}

console.log(templateIt(context, template))

https://repl.it/IdVt/3

Robert Moskal
la source
1

Cette solution fonctionne sans ES6:

function render(template, opts) {
  return new Function(
    'return new Function (' + Object.keys(opts).reduce((args, arg) => args += '\'' + arg + '\',', '') + '\'return `' + template.replace(/(^|[^\\])'/g, '$1\\\'') + '`;\'' +
    ').apply(null, ' + JSON.stringify(Object.keys(opts).reduce((vals, key) => vals.push(opts[key]) && vals, [])) + ');'
  )();
}

render("hello ${ name }", {name:'mo'}); // "hello mo"

Remarque: le Functionconstructeur est toujours créé dans la portée globale, ce qui pourrait entraîner l'écrasement des variables globales par le modèle, par exemplerender("hello ${ someGlobalVar = 'some new value' }", {name:'mo'});

Cruzanmo
la source
0

Puisque nous réinventons la roue sur quelque chose qui serait une fonctionnalité intéressante en javascript.

j'utilise eval() , ce qui n'est pas sécurisé, mais javascript n'est pas sécurisé. J'avoue volontiers que je ne suis pas excellent avec javascript, mais j'avais un besoin, et j'avais besoin d'une réponse alors j'en ai fait une.

J'ai choisi de styliser mes variables avec un @plutôt qu'un $, en particulier parce que je veux utiliser la fonctionnalité multiligne des littéraux sans évaluer jusqu'à ce qu'elle soit prête. La syntaxe variable est donc@{OptionalObject.OptionalObjectN.VARIABLE_NAME}

Je ne suis pas un expert javascript, donc je prendrais volontiers des conseils d'amélioration mais ...

var prsLiteral, prsRegex = /\@\{(.*?)(?!\@\{)\}/g
for(i = 0; i < myResultSet.length; i++) {
    prsLiteral = rt.replace(prsRegex,function (match,varname) {
        return eval(varname + "[" + i + "]");
        // you could instead use return eval(varname) if you're not looping.
    })
    console.log(prsLiteral);
}

Une implémentation très simple suit

myResultSet = {totalrecords: 2,
Name: ["Bob", "Stephanie"],
Age: [37,22]};

rt = `My name is @{myResultSet.Name}, and I am @{myResultSet.Age}.`

var prsLiteral, prsRegex = /\@\{(.*?)(?!\@\{)\}/g
for(i = 0; i < myResultSet.totalrecords; i++) {
    prsLiteral = rt.replace(prsRegex,function (match,varname) {
        return eval(varname + "[" + i + "]");
        // you could instead use return eval(varname) if you're not looping.
    })
    console.log(prsLiteral);
}

Dans ma mise en œuvre réelle, je choisis d'utiliser @{{variable}}. Un autre ensemble d'accolades. Il est peu probable de rencontrer cela de manière inattendue. Le regex pour cela ressemblerait à/\@\{\{(.*?)(?!\@\{\{)\}\}/g

Pour rendre cela plus facile à lire

\@\{\{    # opening sequence, @{{ literally.
(.*?)     # capturing the variable name
          # ^ captures only until it reaches the closing sequence
(?!       # negative lookahead, making sure the following
          # ^ pattern is not found ahead of the current character
  \@\{\{  # same as opening sequence, if you change that, change this
)
\}\}      # closing sequence.

Si vous n'êtes pas expérimenté avec les regex, une règle assez sûre est d'échapper à tous les caractères non alphanumériques, et de ne jamais échapper inutilement aux lettres, car de nombreuses lettres échappées ont une signification spéciale pour pratiquement toutes les saveurs de regex.

Joe régulier
la source
0

Vous devriez essayer ce petit module JS, par Andrea Giammarchi, de github: https://github.com/WebReflection/backtick-template

/*! (C) 2017 Andrea Giammarchi - MIT Style License */
function template(fn, $str, $object) {'use strict';
  var
    stringify = JSON.stringify,
    hasTransformer = typeof fn === 'function',
    str = hasTransformer ? $str : fn,
    object = hasTransformer ? $object : $str,
    i = 0, length = str.length,
    strings = i < length ? [] : ['""'],
    values = hasTransformer ? [] : strings,
    open, close, counter
  ;
  while (i < length) {
    open = str.indexOf('${', i);
    if (-1 < open) {
      strings.push(stringify(str.slice(i, open)));
      open += 2;
      close = open;
      counter = 1;
      while (close < length) {
        switch (str.charAt(close++)) {
          case '}': counter -= 1; break;
          case '{': counter += 1; break;
        }
        if (counter < 1) {
          values.push('(' + str.slice(open, close - 1) + ')');
          break;
        }
      }
      i = close;
    } else {
      strings.push(stringify(str.slice(i)));
      i = length;
    }
  }
  if (hasTransformer) {
    str = 'function' + (Math.random() * 1e5 | 0);
    if (strings.length === values.length) strings.push('""');
    strings = [
      str,
      'with(this)return ' + str + '([' + strings + ']' + (
        values.length ? (',' + values.join(',')) : ''
      ) + ')'
    ];
  } else {
    strings = ['with(this)return ' + strings.join('+')];
  }
  return Function.apply(null, strings).apply(
    object,
    hasTransformer ? [fn] : []
  );
}

template.asMethod = function (fn, object) {'use strict';
  return typeof fn === 'function' ?
    template(fn, this, object) :
    template(this, fn);
};

Démo (tous les tests suivants retournent vrai):

const info = 'template';
// just string
`some ${info}` === template('some ${info}', {info});

// passing through a transformer
transform `some ${info}` === template(transform, 'some ${info}', {info});

// using it as String method
String.prototype.template = template.asMethod;

`some ${info}` === 'some ${info}'.template({info});

transform `some ${info}` === 'some ${info}'.template(transform, {info});
colxi
la source
0

J'ai fait ma propre solution en faisant un type avec une description comme fonction

export class Foo {
...
description?: Object;
...
}

let myFoo:Foo = {
...
  description: (a,b) => `Welcome ${a}, glad to see you like the ${b} section`.
...
}

et ainsi de suite:

let myDescription = myFoo.description('Bar', 'bar');
cesarmarinhorj
la source
0

Au lieu d'utiliser eval, mieux vaut utiliser pour regex

Eval n'est pas recommandé et fortement déconseillé, veuillez donc ne pas l'utiliser ( mdn eval ).

 let b = 10;
 let a="b:${b}";

let response = a.replace(/\${\w+}/ ,b);
conssole.log(response);
Vijay Palaskar
la source
fonctionne pour un, et si j'ai "a is $ {a}, b is {b} ..."?
leachim