How in node to access pug attributes in javascript via a script include

If you found this article then you have a very specific kinda problem, just like I did.

I have a node app on which I am running express to serve pages, i’m also wanting to use Pug to organise my ‘views’.  All of this works fine until you throw javascript into the mix.

Sure, you could just embed the code directly in your .pug page, but that’s not very tidy now is it.

//index.pug
doctype html
  html(lang="en")
    head
    script.
      var foo = bar;
      console.log(foo);
      

Note that there is a . at the end of script.. This allows you to write code directly in the template, rather than have pug evaluate everything within it.

For my purposes I want to keep js away from the template to keep things neat and tidy so it needs to be kept in a specific .js file.

Including a script file in pug/jade is quite simple:

script
  include ./assets/js/tidy.js

But then how would you reference the pug attributes that you have access to within index.pug in the external javascript file?

The first change I had to made was in my app.js file where I was passing in the data to the .pug template. Originally I was passing just the object (so I could then reference the attributes directly) but this makes things tricky for the next step, so I changed the object I was passing in so that I could pick up the data by name.

//app.js
app.get('/product_options', (req, res) => {
  Shopify.get_product_options(req.query.pid).then(product =>
    res.render('product_options', {product: product})
  );
});

This means that I can reference product from my template directly, which will be handy when we stringify its content in order to pass it over to the js file.

//index.pug
doctype html
html(lang="en")
  head
    script.
      var l = !{JSON.stringify(product)}
    script  
      include ../assets/js/product_options.js

Note the !{} interpolation syntax, rather than the standard {}, this prevents any escaping and allows us to take the string value of the products option and write it out to the page in the var l = assignment.

We should now be able to reference l from any external js.

//tidy.js
$( document ).ready(function(){
  console.log(l);
});

It feels like there should be a better way of handling this, so if you know of one please leave a comment and let me know. It might not be pretty but it achieves good separation and allows you to benefit from pugs, otherwise, excellent templating.