Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Express/Node.js : Render custom javascript as response

In my application, I need to provide an API (something like the Google Maps javascript API), through which I can send some custom javascript (with some session and request related information) as the response. The javascript is then used to plot some graphs on the UI. I'm using Express with Jade as my templating engine. The code that I'm currently using is:

app.use('/graph',function(req, res){
   //send out graph data
   var var_name = req.session.var_name //fetch something from session
   var graphData = fetchGraphData(req.query.graph); //function that fetches graph data
   res.contentType("text/javascript");
   res.render(__dirname + '/views/graph.jade', {
     title: "Title", queryStr: JSON.stringify({var_name: var_name, graphData: graphData  })
   });
});

And the jade file:

|  some_var_name = {
|       initGraph : function(divId){
|       //some code here
|       var graphData = !{graphData}
|       // do something                               

As a workaround, I have started each line of the jade file with |, so that jade parses the text as plain text, and doesn't add any html tags! It works fine, but is there a cleaner way to do this? The solution may or may not use Jade!

like image 628
Anuj Arora Avatar asked May 06 '13 22:05

Anuj Arora


2 Answers

You should look into underscore templates. I think that for generating arbitrary text output it would be somewhat cleaner. Jade is purpose-built for rendering HTML.

You could also try Mustache or Handlebars.

Based on your comment, I see you'd like to keep using res.render for rendering the template. consolodate.js adds support for all major template engines to Express. Including Underscore templates, Handlebars, Mustache and Dust, mentioned by @TheHippo.

like image 198
Myrne Stol Avatar answered Sep 20 '22 22:09

Myrne Stol


You may try to define JavaScript functions you need to send to the browser in a separate module, outside of the template, which is probably more correct way from the "concerns separation" point of view. Also if functions are defined in a separate module they can be used both in the server and in the browser.

Then you can convert the functions to strings using its toString() method either in a function that invokes the template or right inside the template, if it supports plain JavaScript which is the case with underscore, EJS and doT templates (I tried both underscore and EJS and ended up using doT which is not only the fastest but very versatile - check it out):

JS code:

// if you send the same functions you may want to convert them to strings in advance
var data = {
    funcStr: func.toString();
};
res.render(view, data);

Template (doT):

<script type="text/javascript">
    func = {{= it.funcStr }};

    // now you can call it here if you want but I would use 
    // separate JavaScript files
    func();
</script>

I use it to send pre-compliled templates to the browser together with the page on the first page load, but I think it can be used in your case too.

As a side question, why can't you just bundle all these functions in a separate JavaScript module and load them as normal script file?

like image 22
esp Avatar answered Sep 21 '22 22:09

esp