martin66
martin66

Reputation: 387

HTML template to Javascript String Literals

thanks for any feedback...

I have an HTML template with JS string literals embedded...

<html>
  <head>
   <title>${title}</title>
  </head>
  <body>
   User name: ${user.name}
  </body>
</html>

I'm then doing...

let temp = require('./template.html')

return temp; // <--- I need this to return the compiled html

How can I get the string literals to render?

Is it even possible to do this?

I need the 'temp' variable to return as a compiled string so that I can insert it to a document.write later on in the code.

Thanks.

Upvotes: 1

Views: 768

Answers (3)

user2033671
user2033671

Reputation:

You can create a new Function to turn it into a string template

return new Function("title","user","return `" + temp + "`;")(title,user);

As pointed out by T.J you will need to know all the variables used in the template and include them as arguments to the function.

Upvotes: 0

T.J. Crowder
T.J. Crowder

Reputation: 1074148

Those aren't string literals. They look like tokens in JavaScript's template literals.

Is it even possible to do this?

If you mean that you want to load the HTML dynamically and then use it as a template literal, you only have two choices for doing that:

  • Use a templating library (or write your own templating code) that parses the string and handles those tokens

  • Use eval / new Function

eval (or new Function) is not evil when used with your own content (and performance costs have been vastly overstated), though I'm not saying I'd recommend it. But this is how you'd do it (assuming that in your stack, require('./template.html') will give you a string with the contents):

let temp = require('./template.html');
const template = "`" + temp.replace(/`/g, "\\`") + "`";

(There may be more escaping you want to do there, I just handled backticks.)

Then when you have the relevant tokens in scope:

const str = eval(template);

Live Example:

let temp = document.getElementById("template").textContent.trim();
const template = "`" + temp.replace(/`/g, "\\`") + "`";

// Using it
const title = "This is the title";
const user = {name: "Joe Blogs"};
const str = eval(template);
console.log(str);
<script type="text/template" id="template">
<html>
  <head>
   <title>${title}</title>
  </head>
  <body>
   User name: ${user.name}
  </body>
</html>
</script>


Why eval instead of new Function above? So that the code wrapping up the template for use doesn't have to know the names title and user. new Function still allows arbitrary code execution, just like eval, so you still can't use it except on your own content...

Upvotes: 1

kharhys
kharhys

Reputation: 257

Yes, it is possible with some minor modification to your code. First rename template.html to template.js and change its content to

exports default ({title, user}) =>`
    <html>
       <head>
        <title>${title}</title>
       </head>
       <body>
         User name: ${user.name}
       </body>
    </html>`

Then let temp = require('./template.js') will assign a function to temp that you should call with your context. Something like

let ctx = { title: 'titre', user: { name: 'alice' } }
let compiled = temp(ctx) // your compiled template

Check out yo-yo a library that works around this concept

Upvotes: 0

Related Questions