30

I want to use a statically defined template for URL building.

I'm trying to use ES6 string interpolation feature for this

var template = "http://example.com/?name=${name}&age=${age}";

var name = "John";
var age = "30";

var url = `${template}`;

Expected result: http://example.com/?name=John&age=23

Actual result: http://example.com/?name=${name}&age=${age}

In case this can't be done with string interpolation is there any better method than String.prototype.replace like

var url = template.replace(/\${name}/,"John").replace(/\${age}/, 23);
humkins
  • 9,635
  • 11
  • 57
  • 75
  • 5
    That's the way they work; template expansion does not recursively expand substituted strings. – Pointy Feb 16 '17 at 16:44
  • `name` is defined after template string is defined. `template` is not a template literal. – guest271314 Feb 16 '17 at 16:55
  • Similar question, albeit framed differently: http://stackoverflow.com/q/30003353/215552 – Heretic Monkey Feb 16 '17 at 16:57
  • 2
    It 100% can recursively assign strings - top comment is wrong. – Phil Feb 16 '17 at 17:04
  • 2
    Important question . . . you never actually state if the if the order of your variable definition is important here . . . do you **need** to define "name" and "age" after the template? – talemyn Feb 16 '17 at 17:06
  • It seems the variables used within a string template need to be defined before its own definition. Otherwise all instances of variables will be replaced with `undefined`. – evolutionxbox Feb 16 '17 at 17:19
  • The word "template" has caused all kinds of confusion. Template literals do not create templates in the same way as templating libraries like Handlebars: all they do is string interpolation. – lonesomeday Feb 16 '17 at 18:54

6 Answers6

30

The variables are substituted at the moment of evaluation of the literal, so you can't create a universal template that can be substituted with variables later:

var template = `http://example.com/?name=${name}&age=${age}`;
var name = "John";
var age = "30";

console.log( template ); // "http://example.com/?name=undefined&age=undefined"

Edit: fiddle for those who reuse a console session and have the variables defined from previous experiments: https://jsfiddle.net/nwvcrryt/

You also can not convert a string literal "My name is ${name}" to a template like what you're trying to do.

You can, however, use a function that takes the name and age and returns the desired url:

const formatUrl = (name, age) => `http://example.com/?name=${name}&age=${age}`;
let name = "John";
let age = "30";
let url = formatUrl( name, age ); // "http://example.com/?name=John&age=30"
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
pawel
  • 35,827
  • 7
  • 56
  • 53
  • This isn't correct. If you run the exact code you pasted in a browser you get `"http://example.com/?name=John&age=undefined"` in the console – Phil Feb 16 '17 at 16:54
  • 3
    @Phil nope, it shpuld be `undefined` twice: https://jsfiddle.net/nwvcrryt/ - are you sure you haven't reused the same console and have defined the name before? – pawel Feb 16 '17 at 16:56
  • That is only because they are assigned AFTER the template. – Phil Feb 16 '17 at 16:58
  • 1
    @Phinl which is why I demonstrate it as the flaw in OP approach (to have universal template that can be substituted with variables at run-time, which is not how template literals work). – pawel Feb 16 '17 at 17:00
16

Here is how you would handle this issue if the values came after and you still wanted to use the template:

var template = (name, age) => `http://example.com/?name=${name}&age=${age}`;

// these come after template is defined
var name = "John";
var age = "30";

console.log(template(name, age));

This is if the question was in regards to recursion:

You used double quotes " instead of a backtick `

It will work otherwise:

var name = "John";
var age = "30";
var template = `http://example.com/?name=${name}&age=${age}`

var url = `${template}`;

https://jsfiddle.net/kab48ht5/


If all you're trying to do is get some values into a proper URL format, you can try and follow this solution: https://stackoverflow.com/a/22678412/185672

Community
  • 1
  • 1
Phil
  • 10,948
  • 17
  • 69
  • 101
  • 3
    Maybe I'm the one who misunderstood, but I interpret the question as "I want to have a template of the URL then build the urls by filling the variables later" (as in: the template should be re-usable, not a one-off call). – pawel Feb 16 '17 at 17:05
  • 2
    @Phil How much can this be helpful? Variable values are unknown during the template definition and are defined during the runtime. – humkins Feb 16 '17 at 17:06
  • 1
    Yeah, I don't understand why this is particularly helpful and I took as someone who is trying to understand ES6's new string interpolation... honestly I would probably just encode an object otherwise – Phil Feb 16 '17 at 17:11
  • Oh, hahaha, I understand now. Fixed. – Phil Feb 16 '17 at 17:37
8

As above mentioned, it is impossible to use variable as Template, perhaps, if you really need to implement it, and your environment is secure, you can use eval

var template = "http://example.com/?name=${name}&age=${age}";

var name = "John";
var age = "30";

var url = eval(`\`${template}\``);

But again, use eval with caution. I recommend you to read this or similar articles before you start using eval in your code.

PS. I'm sure, there will be many people who'll downvote my answer because of it :D

Kostanos
  • 9,615
  • 4
  • 51
  • 65
6

Maybe I'm misunderstanding your issues/use case, but you should be able to do that without the "template" variable. . . just assign the value that you currently have assigned to "template" directly to "url" and use the backticks instead of quotes:

var name = "John";
var age = "30";

var url = `http://example.com/?name=${name}&age=${age}`;

That results in: http://example.com/?name=John&age=30

talemyn
  • 7,822
  • 4
  • 31
  • 52
  • 2
    This will not work since variables are assigned after the template – jetpackpony Feb 16 '17 at 16:55
  • Yeah, sorry . . . I meant to switch the order and forgot. Fixed it. I guess what I need to know was is the order of the variable important to him here, or is he just trying to get the interpolation to work? – talemyn Feb 16 '17 at 17:01
  • I think it's both since without the proper order the interpolation doesn't work :-) – jetpackpony Feb 16 '17 at 17:05
  • Kind of an important detail to know . . . they are two very different problems to solve (i.e., "How do I use string interpolation?" vs. "How can I define a string template **before** the variables that it uses?"). – talemyn Feb 16 '17 at 17:11
4

How about this:

function sFormat(template){
    return data => {
        return Object.keys(data)
                     .reduce((acc, key)=>acc.replaceAll(`\$\{${key}\}`, data[key]), template)
    }
}

let first = "John"
let last = "Doe"
let age = 55
let someData = { a: 1, b: "foo" }

let template = sFormat('Hi ${first}! Your full name: ${first} ${last}, Age: ${age}, Data: ${someData}')
console.log(template({ first, last, age, someData }))

// "Hi John! Your full name: John Doe, Age: 55, Data: [object Object]"
-1

You'd use it like this, directly into the string.

Docs here: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Template_literals - credit to @jetpackpony

const name = 'John'
const age = 23

console.log(`Hi my name is ${name}`)

console.log(`http://example.com/?name=${name}&age=${age}`)

@Phil has presented a quick solution to the recursion problem, as long as you use back ticks on the initial template declaration.

With regards to the initial question (and the lack of a clear-cut answer) perhaps it's not explained enough. If you declare the template vars after the template literal, you cannot use them.

G0dsquad
  • 4,230
  • 1
  • 17
  • 22
  • 1
    Could you also add a link to the docs to your answer please: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Template_literals – jetpackpony Feb 16 '17 at 16:57