Können ES6-Vorlagenliterale zur Laufzeit ersetzt (oder wiederverwendet) werden?

128

tl; dr: Ist es möglich, eine wiederverwendbare Vorlage wörtlich zu machen?

Ich habe versucht, Vorlagenliterale zu verwenden, aber ich glaube, ich verstehe es einfach nicht und jetzt bin ich frustriert. Ich meine, ich denke ich verstehe es, aber "es" sollte nicht so sein, wie es funktioniert oder wie es werden sollte. Es sollte anders werden.

Alle Beispiele, die ich sehe (sogar mit Tags versehene Vorlagen), erfordern, dass die "Ersetzungen" zur Deklarationszeit und nicht zur Laufzeit erfolgen, was mir für eine Vorlage völlig nutzlos erscheint. Vielleicht bin ich verrückt, aber eine "Vorlage" ist für mich ein Dokument, das Token enthält, die ersetzt werden, wenn Sie es verwenden, nicht wenn Sie es erstellen, sonst ist es nur ein Dokument (dh eine Zeichenfolge). Eine Vorlage wird mit den Token als Token gespeichert und diese Token werden ausgewertet, wenn Sie ... sie auswerten.

Jeder zitiert ein schreckliches Beispiel ähnlich:

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

Das ist schön, aber wenn ich es schon weiß a, würde ich einfach return 'Worthless asd'oder return 'Worthless '+a. Was ist der Punkt? Ernsthaft. Okay, der Punkt ist Faulheit; weniger Pluspunkte, mehr Lesbarkeit. Toll. Aber das ist keine Vorlage! Nicht IMHO. Und MHO ist alles was zählt! Das Problem, IMHO, ist, dass die Vorlage ausgewertet wird, wenn sie deklariert wird. Wenn Sie dies tun, IMHO:

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

Da expletivenicht deklariert ist, gibt es so etwas wie aus My undefined template. Super. Zumindest in Chrome kann ich die Vorlage nicht einmal deklarieren. es wird ein Fehler ausgegeben, weil expletivenicht definiert ist. Was ich brauche, ist in der Lage zu sein, die Ersetzung nach dem Deklarieren der Vorlage durchzuführen:

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

Ich sehe jedoch nicht, wie dies möglich ist, da dies keine wirklichen Vorlagen sind. Selbst wenn Sie sagen, ich sollte Tags verwenden, nein, sie funktionieren nicht:

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

Dies alles hat mich zu der Überzeugung geführt, dass Vorlagenliterale schrecklich falsch benannt sind und so genannt werden sollten, wie sie wirklich sind: Heredocs . Ich denke, der "wörtliche" Teil hätte mich darauf hinweisen sollen (wie in, unveränderlich)?

Vermisse ich etwas Gibt es eine (gute) Möglichkeit, eine wiederverwendbare Vorlage wörtlich zu machen?


Ich gebe Ihnen wiederverwendbare Vorlagenliterale :

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

Und hier ist eine naive "Helfer" -Funktion ...

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

...um es besser zu machen".

Ich neige dazu, sie Template-Gutenals zu nennen, weil sie in diesem Bereich verdrehte Gefühle hervorrufen.

Josh
quelle
1
Es unterstützt das Durchstreichen (aber nicht in Kommentaren wie diesen). Fügen Sie Ihren Text in ein <strike>Tag ein.
Pointy
ES6-Vorlagenliterale dienen hauptsächlich der altmodischen String-Interpolation. Wenn Sie dynamische Vorlagen wünschen, verwenden Sie Lenker usw. oder die mit Tags versehene Vorlagenlösung von Pointy.
Joews
1
Vorlagenzeichenfolgen sind trotz des Namens keine Vorlagen . Siehe auch Ausführung für ES6-Vorlagenstrings verschieben
Bergi
8
Könnten Sie bitte Ihren Beitrag etwas weniger garantieren? Es sieht auch so aus, als wollten Sie ein Tutorial im Q & A-Format schreiben. Wenn Sie dies getan haben, entfernen Sie bitte den Teil " Ich gebe Ihnen ... " aus Ihrer Frage und veröffentlichen Sie ihn als Antwort .
Bergi
Ich stelle fest, dass es hier viele gute Antworten gibt. Vielleicht einen akzeptieren.
Abalter

Antworten:

86

Damit diese Literale wie andere Template-Engines funktionieren, muss es eine Zwischenform geben.

Der beste Weg, dies zu tun, ist die Verwendung des FunctionKonstruktors.

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

Wie bei anderen Template-Engines können Sie diese Zeichenfolge von anderen Stellen wie einer Datei abrufen.

Bei dieser Methode kann es Probleme geben, z. B. sind Vorlagen-Tags schwer zu verwenden. Diese können jedoch hinzugefügt werden, wenn Sie klug sind. Aufgrund der späten Interpolation können Sie auch keine Inline-JavaScript-Logik verwenden. Dies kann auch mit einigem Nachdenken behoben werden.

Quentin Engles
quelle
8
Nett! Sie können sogar verwendennew Function(`return \`${template}\`;`)
Ruben Stolk
Und diese Vorlagen können zusammengesetzt oder durch Argumente "eingeschlossen" werden, indem eine Methode aufgerufen oder das kompilierte Ergebnis einer anderen Vorlage übergeben wird.
Quentin Engles
Quentin, was bedeutet "keine Vorlagen-Tags"? Vielen Dank!
Mikemaccana
10
Beachten Sie, dass diese Vorlagenzeichenfolge für die Transpilation (dh das Webpack) etwas "versteckt" ist und sich daher NICHT clientseitig in etwas ausreichend Kompatibles (dh IE11) umwandelt ...!
Frank Nocke
9
XSS-Sicherheitslücke ? Details in DIESEM JSFIDDLE
Kamil Kiełczewski
64

Sie können eine Vorlagenzeichenfolge in eine Funktion einfügen:

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

Mit einer getaggten Vorlage können Sie dasselbe tun:

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"

Die Idee ist, den Vorlagenparser die konstanten Zeichenfolgen von den variablen "Slots" trennen zu lassen und dann eine Funktion zurückzugeben, die alles basierend auf einem neuen Wertesatz jedes Mal wieder zusammenfügt.

Spitze
quelle
3
@FelixKling das kann sein; Ich werde es überprüfen und reparieren, wenn ja. edit ja es sieht so aus, als hätte ich einen wesentlichen Teil des Beispiels ausgelassen, nämlich die "wiederverwendbare" Funktion :)
Pointy
@FelixKling Ich bin mir nicht sicher, was ich tun soll, weil ich mich überhaupt nicht erinnern kann, was ich damals gedacht habe!
Pointy
1
Ja, es macht nicht viel Sinn, entweder tbh zu sein;) Sie können es jederzeit entfernen ... aber es reusablekönnte so implementiert werden, dass es eine Funktion zurückgibt, und Sie würden ${0}und ${1}innerhalb des Literals anstelle von ${a}und verwenden ${b}. Dann können Sie diese Werte verwenden, um auf die Argumente der Funktion zu verweisen, ähnlich wie Bergi es in seinem letzten Beispiel getan hat : stackoverflow.com/a/22619256/218196 (oder ich denke, es ist im Grunde dasselbe).
Felix Kling
1
@FelixKling OK Ich denke, ich habe mir etwas ausgedacht, das zumindest vage dem OP entspricht.
Pointy
3
Mit Tags versehene Vorlagen können sehr leistungsfähig sein, wenn das Ergebnis tatsächlich keine Zeichenfolge ist. In einem meiner Projekte verwende ich es beispielsweise zur AST-Knoteninterpolation. Beispielsweise kann expression`a + ${node}`ein BinaryExpression-Knoten mit einem vorhandenen AST-Knoten erstellt werden node. Intern fügen wir einen Platzhalter ein, um gültigen Code zu generieren, analysieren ihn als AST und ersetzen den Platzhalter durch den übergebenen Wert.
Felix Kling
45

Der wahrscheinlich sauberste Weg, dies zu tun, sind Pfeilfunktionen (da wir zu diesem Zeitpunkt bereits ES6 verwenden).

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"

... und für getaggte Vorlagenliterale:

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"

Dies vermeidet auch die Verwendung von eval()oder Function()was Probleme mit Compilern verursachen und viel Verlangsamung verursachen kann.

PilotInPyjamas
quelle
Ich denke, dies ist eines der besten, weil Sie einige Codes in die Funktion einfügen können, um einige Dinge myTagzu tun. Verwenden Sie beispielsweise die Eingabeparameter als Schlüssel zum Zwischenspeichern der Ausgabe.
WOW
Ich denke, das ist die beste Antwort. Sie können der Pfeilfunktion auch Parameter hinzufügen, was sie meiner Meinung nach noch sauberer macht : var reusable = (value: string) => `Value is ${value}`.
Haggisandchips
13

Antwort 2019 :

Hinweis : Die Bibliothek erwartete ursprünglich, dass Benutzer Zeichenfolgen bereinigen, um XSS zu vermeiden. In Version 2 der Bibliothek müssen Benutzerzeichenfolgen nicht mehr bereinigt werden (was Webentwickler sowieso tun sollten), da dies evalvollständig vermieden wird .

Das es6-dynamic-templateModul auf npm macht das.

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

Im Gegensatz zu den aktuellen Antworten:

  • Es werden ES6-Vorlagenzeichenfolgen verwendet, kein ähnliches Format. Update Version 2 verwendet ein ähnliches Format anstelle von ES6-Vorlagenzeichenfolgen, um zu verhindern, dass Benutzer nicht bereinigte Eingabezeichenfolgen verwenden.
  • Es muss nicht thisin der Vorlagenzeichenfolge
  • Sie können die Vorlagenzeichenfolge und die Variablen in einer einzigen Funktion angeben
  • Es ist ein gewartetes, aktualisierbares Modul und keine Copypasta von StackOverflow

Die Verwendung ist einfach. Verwenden Sie einfache Anführungszeichen, da die Vorlagenzeichenfolge später aufgelöst wird!

const greeting = fillTemplate('Hi ${firstName}', {firstName: 'Joe'});
Mikemaccana
quelle
Wenn Sie dies mit React Native verwenden, wird es speziell unter Android kaputt gehen. Die Laufzeit des Android-Knotens unterstützt keine dynamischen Vorlagen, sondern nur vorgefertigte
Oliver Dixon,
1
Dies ist eine Lösung, die ich in meinen persönlichen Projekten verwende und die einwandfrei funktioniert. Ich halte es eigentlich für eine schlechte Idee, zu viele Bibliotheken zu verwenden, insbesondere für kleine Dienstprogramme wie dieses.
Oliver Dixon
1
XSS-Sicherheitslücke ? Details in DIESEM FIDDLE
Kamil Kiełczewski
1
@kamil Nur XSS, wenn Sie a) Benutzern die Möglichkeit zum Erstellen geben b) die Eingabezeichenfolgen nicht bereinigen. Ich werde eine Warnung hinzufügen, dass Benutzer Benutzereingaben bereinigen sollten.
Mikemaccana
1
Dabei werden keine es6-Vorlagenliterale aus der Ferne verwendet. Versuchen Sie 10 * 20 = ${10 * 20}, es könnte ein ähnliches Format sein, aber es ist nicht einmal aus der Ferne es6 Template-Literale
Gman
12

Ja, Sie können dies tun, indem Sie Ihre Zeichenfolge mit der Vorlage als JS von Function(oder eval) analysieren. Dies wird jedoch nicht empfohlen und ermöglicht einen XSS-Angriff

Stattdessen können Sie Objektfelder wie folgt sicher auf dynamische Weise objin die Vorlage einfügenstr

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

Kamil Kiełczewski
quelle
Dies ist die Methode, die ich benutze und die gut funktioniert hat. Gutes Beispiel! Tut das? nach dem * in der RegEx-Hilfe? Ich bin kein RegEx-Experte, aber ich vermute, da das * null oder mehr bedeutet (und Sie in diesem Fall das "mehr" wollen), besteht keine Notwendigkeit für die gierige Einschränkung?
Gen1-1
@ Gen1-1 das .*?bedeutet nicht gierig - wenn Sie entfernen, "?"dann Snippet wird falsches Ergebnis geben
Kamil Kiełczewski
Du hast recht, mein Fehler. Ich verwende $ nicht in meinen Vorlagen und verwende RegEx: / {(\ w *)} / g, weil ich nie Leerzeichen im Tag habe, aber. *? funktioniert auch. Ich benutze: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 sind auch verschachtelte Daten möglich? wie data = { a: 1, b: { c:2, d:3 } }-> b.c?
Muescha
1
@muescha Sie würden die Zeile ändern: value = data [key], um die Rekursion zu verwenden und Ihr gesamtes Datenobjekt und verschachtelte Objekte zu durchsuchen, bis Sie die Eigenschaft gefunden haben. Beispiele: codereview.stackexchange.com/questions/73714/... und mikedoesweb.com/2016/es6-depth-first-object-tree-search
Gen1-1
9

Vereinfachung der Antwort von @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);

Subcoder
quelle
Dieser Code ist selbsterklärender als die führende Antwort.
Habe
Dies sollte es Ihnen ermöglichen, Variablen oder externe Dateien (in NodeJS) als Vorlagen zu verwenden oder sie zur Laufzeit dynamisch zu erstellen. Ohne die Verwendung von eval.
B01
XSS-Sicherheitslücke ? Spielen Sie HIER mit bösartigem Code (Variable var hosting) .
Kamil Kiełczewski
7

Wenn Sie möchten , um die Variablen in der Vorlage referenzieren nicht verwenden geordneten Parameter oder Kontext / Namensräume, zum Beispiel ${0}, ${this.something}oder ${data.something}können Sie eine Template - Funktion haben , die Pflege des Scoping für Sie nimmt.

Beispiel, wie Sie eine solche Vorlage aufrufen können:

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

Die Vorlagenfunktion:

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

Die Besonderheit in diesem Fall ist, dass Sie nur eine Funktion übergeben müssen (im Beispiel habe ich eine Pfeilfunktion verwendet), die das ES6-Vorlagenliteral zurückgibt. Ich denke, es ist ein kleiner Kompromiss, die Art von wiederverwendbarer Interpolation zu erhalten, nach der wir suchen.

Hier ist es auf GitHub: https://github.com/Adelphos/ES6-Reuseable-Template

Metamorphasi
quelle
3
Das ist gut, aber die Minimierung (vals, func usw.) ist unnötig, 'cb' ist kein Rückruf (dies ist vollständig Synchronisierungscode), und Sie können einfach Object.values()undObject.keys()
mikemaccana
3

Die kurze Antwort lautet: Verwenden Sie einfach _.template in 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!'
aGuegu
quelle
3

Vermisse ich etwas Gibt es eine [gute] Möglichkeit, eine wiederverwendbare Vorlage wörtlich zu machen?

Vielleicht bin ich etwas fehlt, weil meine Lösung für dieses Problem zu mir so offensichtlich scheint , dass ich sehr überrascht , niemand bin, der schrieb bereits in einem so alten Frage.

Ich habe einen fast einzeiligen dafür:

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

Das ist alles. Wenn ich eine Vorlage wiederverwenden und die Auflösung der Ersetzungen verschieben möchte, mache ich einfach:

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

Durch Anwenden dieses Tags wird ein 'function'(anstelle von a 'string') zurückgegeben, das alle an das Literal übergebenen Parameter ignoriert. Dann kann es später mit neuen Parametern aufgerufen werden. Wenn ein Parameter kein entsprechendes Ersetzen hat, wird es 'undefined'.


Erweiterte Antwort

Dieser einfache Code ist funktionsfähig, aber wenn Sie ein ausgefeilteres Verhalten benötigen, kann dieselbe Logik angewendet werden und es gibt endlose Möglichkeiten. Du könntest:

  1. Verwenden Sie die Originalparameter:

Sie können die an das Literal übergebenen Originalwerte in der Konstruktion speichern und beim Anwenden der Vorlage auf kreative Weise verwenden. Sie können zu Flags, Typvalidatoren, Funktionen usw. werden. In diesem Beispiel werden sie als Standardwerte verwendet:

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

Dann:

    > t = deferWithDefaults`My template is: ${'extendable'} and ${'versatile'}`;
    > t('awesome');                 // 'My template is: awesome and versatile' 
  1. Schreiben Sie eine Vorlagenfabrik:

Schließen Sie dazu diese Logik in eine Funktion ein, die als Argument eine benutzerdefinierte Funktion erwartet, die in der Reduktion angewendet werden kann (beim Verbinden der Teile des Vorlagenliteral) und eine neue Vorlage mit benutzerdefiniertem Verhalten zurückgibt.

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

Dann könnten Sie beispielsweise Vorlagen schreiben, die beim Schreiben von eingebettetem HTML, CSS, SQL, Bash ... automatisch entkommen oder Parameter bereinigen.

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

Mit dieser naiven (ich wiederhole, naiven! ) SQL-Vorlage könnten wir Abfragen wie diese erstellen:

    > 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. Akzeptieren Sie benannte Parameter für die Substitution: Eine nicht so schwierige Übung, basierend auf dem, was bereits gegeben wurde. Diese andere Antwort enthält eine Implementierung .

  2. Stellen Sie sicher, dass sich das Rückgabeobjekt wie 'string'folgt verhält : Nun, das ist umstritten, könnte aber zu interessanten Ergebnissen führen. In dieser anderen Antwort gezeigt .

  3. Lösen Sie Parameter im globalen Namespace am Aufrufstandort auf:

Ich gebe Ihnen wiederverwendbare Vorlagenliterale:

Nun, das ist, was OP gezeigt hat, ist sein Nachtrag, mit dem Befehl evil, ich meine , eval. Dies kann ohne das evalDurchsuchen des übergebenen Variablennamens in das globale (oder Fenster-) Objekt erfolgen. Ich werde nicht zeigen, wie es geht, weil es mir nicht gefällt. Verschlüsse sind die richtige Wahl.

Rodrigo Rodrigues
quelle
2

Dies ist mein bester Versuch:

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

Um zu verallgemeinern:

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

Wenn Sie E6 nicht ausführen, können Sie auch Folgendes tun:

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

Dies scheint etwas prägnanter zu sein als die vorherigen Antworten.

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

abalter
quelle
2

Im Allgemeinen bin ich gegen das Böse eval(), aber in diesem Fall macht es Sinn:

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

console.log(populated);         // shows 1.2

Wenn Sie dann die Werte ändern und eval () erneut aufrufen, erhalten Sie das neue Ergebnis:

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

console.log(populated);         // shows 3.4

Wenn Sie es in einer Funktion wollen, dann kann es so geschrieben werden:

function populate(a, b){
  return `${a}.${b}`;
}
Isapir
quelle
Wenn Sie eine Funktion schreiben, die die Vorlage enthält, sollten Sie diese definitiv nicht verwenden eval.
Bergi
@Bergi Warum? Wie unterscheidet es sich von Ihrer Implementierung?
Isapir
2
Die Gründe, die ich "zu kennen scheine", gelten für jeden dynamisch erstellten Code. Das Schreiben der Funktion, damit das Ergebnis ohne eval()expliziten Aufruf erstellt wird, ist genau das gleiche wie eval(), daher hat es keinen Vorteil, da der Code nur schwerer zu lesen ist.
Isapir
1
Genau. Und da Ihre populateFunktion den Code nicht dynamisch erstellt, sollte sie nicht evalmit all ihren Nachteilen verwendet werden.
Bergi
6
Ihre Funktion könnte nur function populate(a,b) { return `${a}.${b}`; }die
Bewertung
1

AKTUALISIERT: Die folgende Antwort ist auf einzelne Variablennamen beschränkt, daher sind Vorlagen wie: 'Result ${a+b}'für diesen Fall nicht gültig. Sie können jedoch immer mit den Vorlagenwerten spielen:

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

URSPRÜNGLICHE ANTWORT:

Basierend auf den vorherigen Antworten, aber Schaffung einer "freundlicheren" Dienstprogrammfunktion:

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

    return tpl_func.call(params);
}

Sie können es wie folgt aufrufen:

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

Und die resultierende Zeichenfolge sollte sein:

'This is a test: Hola, second param: Hi'
Roberto
quelle
Was ist mit einer solchen Vorlage? `Result: ${a+b}`
Atiris
1
Hallo @Atiris, du hast recht, das ist eine Einschränkung, ich habe meine Antwort aktualisiert.
Roberto
1

Wenn Sie nach etwas ziemlich Einfachem suchen (nur feste Variablenfelder, keine Berechnungen, Bedingungen…), das aber auch clientseitig in Browsern ohne Unterstützung für Vorlagenzeichenfolgen wie IE 8,9,10,11 funktioniert

Auf geht's:

fillTemplate = function (templateString, templateVars) {
    var parsed = templateString;
    Object.keys(templateVars).forEach(
        (key) => {
            const value = templateVars[key]
            parsed = parsed.replace('${'+key+'}',value)
        }
    )
    return parsed
}
Frank Nocke
quelle
Dadurch wird für jede Variable nachgeschlagen. Es gibt einen anderen Weg, der alle Vorkommen auf einmal ersetzt, die ich in diesem Modul implementiert habe: safe-es6-template
Aalex Gabi
1

Ich war verärgert über die zusätzliche Redundanz, die this.jedes Mal beim Tippen erforderlich ist , und fügte daher Regex hinzu, um Variablen .anach Belieben zu erweitern this.a.

Lösung:

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

Verwendung als solche:

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

Ich veröffentliche nur ein npm-Paket, das diesen Job einfach erledigen kann. Tief inspiriert von dieser Antwort .

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

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

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

Das Gerät ist tödlich einfach. Ich wünschte, es würde dir gefallen.


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

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

Sie können die Inline-Pfeilfunktion wie folgt verwenden, Definition:

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

Verwendung:

console.log(template('my replaced string'));
EladTal
quelle
1

Laufzeitvorlagenzeichenfolge

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

Prüfung

console.debug(templateString('hello ${word} world', {word: 'wonderful'}));
Dennis T.
quelle
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

Raul
quelle