Konvertieren Sie eine Zeichenfolge in eine Vorlagenzeichenfolge

147

Ist es möglich, eine Vorlagenzeichenfolge als normale Zeichenfolge zu erstellen?

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

und konvertieren Sie es dann in eine Vorlagenzeichenfolge

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

ohne eval, new Functionund andere Mittel der dynamischen Code - Generierung?

KOLANICH
quelle
5
Haben Sie einen Weg gefunden, dies zu erreichen? Ich muss es vielleicht eines Tages tun und bin neugierig zu wissen, wozu Sie gekommen sind.
Bryan Rayner
@BryanRayner nimmt an, Ihr js-Programm versucht, Daten von der Rest-API abzurufen, deren URL sich in einer config.js-Datei als Zeichenfolge "/ resources / <Ressourcen-ID> / update /" befindet, und Sie setzen "resource_id" dynamisch aus Ihrem Programm . Sofern Sie diese URL nicht in Teile aufteilen und in verschiedenen Bereichen speichern möchten, benötigen Sie eine Art Verarbeitung von Zeichenfolgenvorlagen.
Ryu_hayabusa
Anstatt eval besser zu verwenden, wird es verwendet, um Eval neu zu formulieren. Es wird nicht empfohlen und dringend davon abgeraten. Verwenden Sie es daher nicht. Developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…! sei b = 10; sei a = "b: $ {b}"; let response = a.replace (/ \ $ {\ w +} /, b); conssole.log (Antwort);
Vijay Palaskar

Antworten:

79

Da Ihre Vorlagenzeichenfolge bdynamisch (zur Laufzeit) auf die Variable verweisen muss, lautet die Antwort: NEIN, ohne dynamische Codegenerierung ist dies nicht möglich.

Aber damit ist evales ziemlich einfach:

let tpl = eval('`'+a+'`');
Alexpods
quelle
7
eval ist unsicher, ebenso wie andere Mittel zur dynamischen Codegenerierung
KOLANICH
8
@KOLANICH Insbesondere für diesen Fall - Escape-Anführungszeichen in der aZeichenfolge und es wird viel weniger unsicher sein : let tpl = eval('`'+a.replace(/`/g,'\\`')+'`');. Ich denke, wichtiger ist, evaldass der Compiler Ihren Code nicht optimiert. Aber ich denke, dass es für diese Frage irrelevant ist.
Alexpods
3
Tatsächlich können Sie Funktionen auch in Vorlagenzeichenfolgen ausführen.
KOLANICH
9
@KOLANICH Sorry du magst nicht eval. Denken Sie jedoch daran, dass ein Vorlagenliteral selbst eine Form von ist eval. Zwei Beispiele: var test = Result: ${alert('hello')}; var test = Result: ${b=4}; Beide führen am Ende beliebigen Code im Kontext des Skripts aus. Wenn Sie beliebige Zeichenfolgen zulassen möchten, können Sie dies auch zulassen eval.
Manngo
6
Achtung. Da so etwas wie babel dies nicht
transpilieren
79

In meinem Projekt habe ich so etwas mit ES6 erstellt:

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);

UPDATE Ich habe die lodash-Abhängigkeit entfernt. ES6 verfügt über äquivalente Methoden zum Abrufen von Schlüsseln und Werten.

Mateusz Moska
quelle
1
Hallo, Ihre Lösung funktioniert hervorragend, aber wenn ich sie in React Native (Erstellungsmodus) verwendet habe, wird ein Fehler ausgegeben : Ungültiges Zeichen '`' , obwohl es funktioniert, wenn ich im Debug-Modus ausgeführt werde. Sieht aus wie, Babel Problem, irgendeine Hilfe?
Mohit Pandey
@MohitPandey Ich habe den gleichen Fehler erhalten, als ich Tests dieses Codes unter PhantomJS ausgeführt habe und er unter Chrome übergeben wurde. Wenn dies der Fall ist, gibt es meiner Meinung nach eine neue Beta-Version von PhantomJS mit besserer Unterstützung für ES6. Sie können versuchen, sie zu installieren.
Mateusz Moska
1
Leider funktioniert es nicht und ich habe einen regulären Ausdruck dafür aufgeschrieben. Als Antwort ebenfalls hinzugefügt.
Mohit Pandey
Diese Lösung funktioniert nur, wenn das Back-Tick-Zeichen "` "nicht in der Vorlagenzeichenfolge vorhanden ist
SliverNinja - MSFT
Wenn ich es versuche, habe ich ReferenceError: _ is not defined. Ist es Code nicht ES6 sondern lodashspezifisch oder ...?
xpt
29

Was Sie hier verlangen:

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

ist genau gleichwertig (in Bezug auf Leistung und Sicherheit) eval : der Fähigkeit, eine Zeichenfolge mit Code zu nehmen und diesen Code auszuführen; und auch die Fähigkeit des ausgeführten Codes, lokale Variablen in der Umgebung des Aufrufers zu sehen.

In JS gibt es für eine Funktion keine Möglichkeit, lokale Variablen in ihrem Aufrufer anzuzeigen, es sei denn, diese Funktion ist dies eval(). Function()Kann es sogar nicht.


Wenn Sie hören, dass JavaScript so genannte "Vorlagenzeichenfolgen" enthält, können Sie davon ausgehen, dass es sich um eine integrierte Vorlagenbibliothek wie Moustache handelt. Ist es nicht. Es ist hauptsächlich nur String-Interpolation und mehrzeilige Strings für JS. Ich denke jedoch, dass dies für eine Weile ein weit verbreitetes Missverständnis sein wird. :((

Jason Orendorff
quelle
2
TBH das dachte ich mir. Wäre sehr, sehr praktisch gewesen.
Bryan Rayner
Funktioniert das (noch)? Ich bekomme template is not a function.
Ionică Bizău
2
Der Codeblock oben in dieser Antwort ist ein Zitat aus der Frage. Es funktioniert nicht.
Jason Orendorff
27

Nein, ohne dynamische Codegenerierung ist dies nicht möglich.

Ich habe jedoch eine Funktion erstellt, die eine reguläre Zeichenfolge in eine Funktion umwandelt, die mithilfe interner Vorlagenzeichenfolgen mit einer Wertekarte versehen werden kann.

Generate Template String Gist

/**
 * 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;
})();

Verwendung:

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

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

Hoffe das hilft jemandem. Wenn Sie ein Problem mit dem Code finden, aktualisieren Sie bitte das Gist.

Bryan Rayner
quelle
Vielen Dank! Ich habe dies anstelle einer Javascript-Sprintf-Lösung verwendet.
Seangwright
1
funktioniert nicht für jede Vorlage var test = generateTemplateString('/api/${param1}/${param2}/') console.log(test({param1: 'bar', param2: 'foo'}))zurück/api/bar//
Guillaume Vincent
Danke, behoben. Die Regex enthielt eine einzelne Übereinstimmung von $ {param1} / $ {param2}, wenn es zwei Übereinstimmungen gewesen sein sollten.
Bryan Rayner
Beachten Sie, dass dies in IE11 nicht funktioniert, da die Unterstützung für Back-Ticks fehlt.
am
1
Wenn Vorlagenzeichenfolgen von einem Browser nicht unterstützt werden, funktioniert diese Methode natürlich nicht. Wenn Sie Vorlagenzeichenfolgen in nicht unterstützten Browsern verwenden möchten, würde ich die Verwendung einer Sprache wie TypeScript oder eines Transpilers wie Babel empfehlen. Nur so kann ES6 in alte Browser integriert werden.
Bryan Rayner
9

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

Jeder scheint besorgt über den Zugriff auf Variablen zu sein. Warum nicht einfach übergeben? Ich bin sicher, es wird nicht zu schwierig sein, den variablen Kontext im Aufrufer abzurufen und weiterzugeben. Benutze das https://stackoverflow.com/a/6394168/6563504 , um die Requisiten von obj zu erhalten. Ich kann momentan nicht für dich testen, aber das sollte funktionieren.

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

Geprüft. Hier ist der vollständige Code.

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
quelle
@ s.meijer könntest du das näher erläutern? Ich verwende diesen Code erfolgreich. jsfiddle.net/w3jx07vt
M3D
1
Eine bessere Regex würde es Ihnen ermöglichen, die ${}Zeichen nicht auszuwählen . Versuchen Sie:/(?!\${)([^{}]*)(?=})/g
Eric Hodonsky
@Relic jsfiddle.net/w3jx07vt/2 Ich konnte das nicht zum Laufen bringen. Möchtest du mithelfen und ich werde meinen Beitrag aktualisieren? :)
M3D
Die Art und Weise, wie Sie versuchen, es zu erfassen, hilft nicht viel. Ich habe stattdessen einen String-Ersatz durchgeführt. Anstatt einen Interpolationsschritt hinzuzufügen, kann ich den String als Interp oder String verwenden. Nichts Besonderes, aber es hat funktioniert.
Eric Hodonsky
8

Hier geht es darum, eine Funktion zu haben, die Zugriff auf die Variablen ihres Aufrufers hat. Aus diesem Grund wird Direct direkt evalfür die Vorlagenverarbeitung verwendet. Eine mögliche Lösung wäre, eine Funktion zu generieren, die formale Parameter verwendet, die durch die Eigenschaften eines Wörterbuchs benannt sind, und sie mit den entsprechenden Werten in derselben Reihenfolge aufruft. Ein alternativer Weg wäre, etwas Einfaches wie dieses zu haben:

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

Und für jeden, der den Babel-Compiler verwendet, müssen wir einen Abschluss erstellen, der sich an die Umgebung erinnert, in der er erstellt wurde:

console.log(new Function('name', 'return `' + message + '`;')(name));
Didinko
quelle
Ihr erstes Snippet ist tatsächlich schlimmer als evalweil es nur mit einer globalen nameVariablen
funktioniert
@Bergi Ihre Aussage ist gültig - der Funktionsumfang geht verloren. Ich wollte eine einfache Lösung für das Problem präsentieren und lieferte ein vereinfachtes Beispiel dafür, was getan werden kann. Man könnte sich einfach Folgendes einfallen lassen, um das Problem zu lösen: var template = function() { var name = "John Smith"; var message = "Hello, my name is ${name}"; this.local = new Function('return '+ message +';')();}
didinko
Nein, genau das würde nicht funktionieren - new Functionhat keinen Zugriff auf die var namein der templateFunktion.
Bergi
Der zweite Snip hat mein Problem behoben ... Up Vote von mir! Vielen Dank, dies half bei der Lösung eines vorübergehenden Problems, das wir mit dem dynamischen Routing zu einem Iframe hatten :)
Kris Boyd
7

Hier gibt es viele gute Lösungen, aber noch keine, die die ES6 String.raw-Methode verwenden . Hier ist mein Beitrag. Es hat eine wichtige Einschränkung dahingehend, dass nur Eigenschaften von einem übergebenen Objekt akzeptiert werden, was bedeutet, dass keine Codeausführung in der Vorlage funktioniert.

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. Teilen Sie die Zeichenfolge in nicht argumentative Textteile auf. Siehe Regex .
    parts: ["Hello, ", "! Are you ", " years old?"]
  2. Teilen Sie die Zeichenfolge in Eigenschaftsnamen auf. Leeres Array, wenn die Übereinstimmung fehlschlägt.
    args: ["name", "age"]
  3. Ordnen Sie die Parameter objanhand des Eigenschaftsnamens zu. Die Lösung wird durch eine flache Zuordnung auf einer Ebene begrenzt. Undefinierte Werte werden durch eine leere Zeichenfolge ersetzt, andere falsche Werte werden jedoch akzeptiert.
    parameters: ["John Doe", 18]
  4. Ergebnis nutzen String.raw(...)und zurückgeben.
Pekaaw
quelle
Welchen Wert bietet String.raw aus Neugier hier tatsächlich? Anscheinend erledigen Sie die ganze Arbeit damit, die Zeichenfolge zu analysieren und die Substitutionen zu verfolgen. Unterscheidet sich das stark davon, einfach .replace()wiederholt anzurufen ?
Steve Bennett
Fairer Punkt, @SteveBennett. Ich hatte einige Probleme, eine normale Zeichenfolge in eine Vorlagenzeichenfolge umzuwandeln, und fand eine Lösung, indem ich das Rohobjekt selbst erstellte. Ich denke, es reduziert String.raw auf eine Verkettungsmethode, aber ich denke, es funktioniert ziemlich gut. Ich würde mir aber eine schöne Lösung .replace()wünschen :) Ich denke, Lesbarkeit ist wichtig, also versuche ich, sie zu benennen, während ich selbst reguläre Ausdrücke verwende, um alles zu
verstehen
6

Ähnlich wie Daniels Antwort (und s.meijers Kern ), aber besser lesbar:

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);
}

Hinweis: Dies verbessert das Original von s.meijer geringfügig, da es nicht mit Dingen wie übereinstimmt ${foo{bar}(der reguläre Ausdruck erlaubt nur nicht geschweifte Klammern in ${und }).


UPDATE: Ich wurde nach einem Beispiel gefragt, also los geht's:

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

interpolate('My name is ${name}, and I am ${age}.', replacements)
Matt Browne
quelle
Können Sie ein Beispiel veröffentlichen, das dies tatsächlich verwendet? Dieses Javascript ist mir ein bisschen unverständlich. Ich würde einen regulären Ausdruck von vorschlagen /\$\{(.*?)(?!\$\{)\}/g(um mit geschweiften Klammern im Nest umzugehen). Ich habe eine funktionierende Lösung, bin mir aber nicht sicher, ob sie so portabel ist wie Ihre. Daher würde ich gerne sehen, wie dies auf einer Seite implementiert werden sollte. Meins benutzt auch eval().
Regular Joe
Ich habe auch eine Antwort gepostet, und ich würde mich über Ihr Feedback freuen, wie Sie dies sicherer und leistungsorientierter gestalten können: stackoverflow.com/a/48294208 .
Regular Joe
@ RegularJoe Ich habe ein Beispiel hinzugefügt. Mein Ziel war es, es einfach zu halten, aber Sie haben Recht, wenn Sie mit verschachtelten geschweiften Klammern umgehen möchten, müssen Sie den regulären Ausdruck ändern. Ich kann mir jedoch keinen Anwendungsfall dafür vorstellen, wenn ich eine reguläre Zeichenfolge so auswerte, als wäre es ein Vorlagenliteral (der gesamte Zweck dieser Funktion). Was hattest du im Sinn?
Matt Browne
Außerdem bin ich weder Leistungsexperte noch Sicherheitsexperte. Meine Antwort besteht wirklich nur darin, zwei vorherige Antworten zu kombinieren. Aber ich werde sagen, dass Sie durch die Verwendung evalviel offener für mögliche Fehler sind, die Sicherheitsprobleme verursachen würden, während meine Version lediglich eine Eigenschaft für ein Objekt von einem durch Punkte getrennten Pfad aus nachschlägt, was sicher sein sollte.
Matt Browne
5

Sie können beispielsweise den String-Prototyp verwenden

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

Aber die Antwort auf die ursprüngliche Frage ist auf keinen Fall.

Sarkiroka
quelle
5

Ich mochte die Antwort von s.meijer und schrieb meine eigene Version basierend auf seiner:

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
quelle
1
Ordentlich! Wirklich ordentlich!
xpt
4

Ich benötigte diese Methode mit Unterstützung für Internet Explorer. Es stellte sich heraus, dass die Back Ticks nicht einmal von IE11 unterstützt werden. Ebenfalls; Verwenden evaloder es ist gleichwertig Functionfühlt sich nicht richtig an.

Für den, der es bemerkt; Ich benutze auch Backticks, aber diese werden von Compilern wie babel entfernt. Die von anderen vorgeschlagenen Methoden hängen von der Laufzeit ab. Wie gesagt; Dies ist ein Problem in IE11 und niedriger.

Das habe ich mir also ausgedacht:

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);
  });
}

Beispielausgabe:

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 -
an Meijer
quelle
"Die Verwendung von eval oder einer gleichwertigen Funktion fühlt sich nicht richtig an." ... Ja ... ich stimme zu, aber ich denke, dies ist einer der wenigen Anwendungsfälle, in denen man sagen könnte: "mmhkay, lass es uns verwenden". Bitte überprüfen Sie jsperf.com/es6-string-tmpl - es ist mein praktischer Anwendungsfall. Verwenden Sie Ihre Funktion (mit demselben regulären Ausdruck wie meine) und meine (eval + string literals). Vielen Dank! :)
Andrea Puddu
@AndreaPuddu, Ihre Leistung ist in der Tat besser. Aber dann wieder; Vorlagenzeichenfolgen werden im IE nicht unterstützt. Funktioniert also eval('`' + taggedURL + '`')einfach nicht.
Uhr
"Scheint" besser, würde ich sagen, weil es isoliert getestet wird ... Der einzige Zweck dieses Tests war es, die potenziellen Leistungsprobleme zu erkennen eval. In Bezug auf Vorlagenliterale: Vielen Dank, dass Sie noch einmal darauf hingewiesen haben. Ich benutze Babel, um meinen Code zu transpilieren, aber meine Funktion wird anscheinend immer noch nicht funktionieren 😐
Andrea Puddu
3

Ich kann derzeit keine Kommentare zu vorhandenen Antworten abgeben, daher kann ich Bryan Raynors hervorragende Antwort nicht direkt kommentieren. Daher wird diese Antwort seine Antwort mit einer leichten Korrektur aktualisieren.

Kurz gesagt, seine Funktion kann die erstellte Funktion nicht tatsächlich zwischenspeichern, sodass sie immer neu erstellt wird, unabhängig davon, ob die Vorlage zuvor angezeigt wurde. Hier ist der korrigierte Code:

    /**
     * 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
quelle
3

@Mateusz Moska, die Lösung funktioniert hervorragend, aber wenn ich sie in React Native (Build-Modus) verwendet habe, wird ein Fehler ausgegeben : Ungültiges Zeichen '`' , obwohl sie funktioniert, wenn ich sie im Debug-Modus ausführe.

Also habe ich meine eigene Lösung mit Regex aufgeschrieben.

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)

Demo: https://es6console.com/j31pqx1p/

HINWEIS: Da ich die Grundursache eines Problems nicht kenne, habe ich ein Ticket im reaktionsnativen Repo https://github.com/facebook/react-native/issues/14107 ausgestellt , damit sie es können fix / führe mich ungefähr gleich :)

Mohit Pandey
quelle
Dies unterstützt Vorlagen, die das Back-Tick-Zeichen enthalten. Anstatt jedoch zu versuchen, ein Vorlagenmuster zu erfinden, ist es wahrscheinlich besser, nur Schnurrbart oder ähnliches zu verwenden . Je nachdem, wie komplex Ihre Vorlagen sind, handelt es sich um einen Brute-Force-Ansatz, bei dem Kantenfälle nicht berücksichtigt werden. Der Schlüssel kann ein spezielles Regex-Muster enthalten.
SliverNinja - MSFT
2

Immer noch dynamisch, scheint aber kontrollierter zu sein als nur eine nackte Bewertung zu verwenden:

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
quelle
1

Diese Lösung funktioniert ohne 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"

Beachten Sie das Function Konstruktor wird immer im globalen Bereich erstellt, was möglicherweise dazu führen kann, dass globale Variablen von der Vorlage überschrieben werden, zrender("hello ${ someGlobalVar = 'some new value' }", {name:'mo'});

Cruzanmo
quelle
0

Da wir das Rad auf etwas neu erfinden, das eine schöne Funktion in Javascript wäre.

ich benutze eval() , was nicht sicher ist, aber Javascript ist nicht sicher. Ich gebe gerne zu, dass ich mit Javascript nicht besonders gut umgehen kann, aber ich hatte ein Bedürfnis, und ich brauchte eine Antwort, also habe ich eine gemacht.

Ich habe mich dafür entschieden, meine Variablen @eher mit einem als mit einem zu stilisieren $, insbesondere weil ich die mehrzeilige Funktion von Literalen verwenden möchte, ohne sie auszuwerten, bis sie fertig sind. Die variable Syntax ist also@{OptionalObject.OptionalObjectN.VARIABLE_NAME}

Ich bin kein Javascript-Experte, daher würde ich gerne Ratschläge zur Verbesserung erhalten, aber ...

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);
}

Es folgt eine sehr einfache Implementierung

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);
}

In meiner tatsächlichen Implementierung entscheide ich mich für die Verwendung @{{variable}}. Noch ein Satz Zahnspangen. Es ist absurd unwahrscheinlich, dass dies unerwartet auftritt. Die Regex dafür würde aussehen/\@\{\{(.*?)(?!\@\{\{)\}\}/g

Um das Lesen zu erleichtern

\@\{\{    # 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.

Wenn Sie nicht mit Regex erlebt, eine ziemlich sichere Regel ist jede nicht-alphanumerischen Zeichen zu entkommen, und nicht immer unnötig Buchstaben entkommen so viele entkam Buchstaben besondere Bedeutung zu praktisch alle Varianten von regex haben.

Normaler Joe
quelle
0

Sie sollten dieses winzige JS-Modul von Andrea Giammarchi von github ausprobieren: 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);
};

Demo (alle folgenden Tests geben true zurück):

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
quelle
0

Ich habe meine eigene Lösung gemacht, indem ich einen Typ mit einer Beschreibung als Funktion erstellt habe

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

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

und so:

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

Anstatt eval besser zu verwenden, wird Regex verwendet

Eval ist nicht zu empfehlen und wird dringend empfohlen. Verwenden Sie es daher nicht ( mdn eval ).

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

let response = a.replace(/\${\w+}/ ,b);
conssole.log(response);
Vijay Palaskar
quelle