49

I am completely lost on this; I am using NodeJS to fetch a JSON and I need to pass the variable to my page and have JavaScript use the data.

app.get('/test', function(req, res) {
    res.render('testPage', {
        myVar: 'My Data'
    });

That is my Express code (very simple for testing purposes); now using EJS I want to gather this data which I know to render on the page is simply

<%= myVar %>

But I need to be able to gather this data in JavaScript (if possible within a .js file) but for now just to display the variable in an Alert box I have tried

In Jade it is like alert('!{myVar}') or !{JSON.stringify(myVar)}. Can I do something similar in EJS. I don't need any field like <input type=hidden> and taking the value of the field in javascript. If anyone can help be much appreciated

8 Answers 8

80

You could use this (client-side):

<script>
  var myVar = <%- JSON.stringify(myVar) %>;
</script>

You could also get EJS to render a .js file:

app.get('/test.js', function(req, res) {
  res.set('Content-Type', 'application/javascript');
  res.render('testPage', { myVar : ... });
});

However, the template file (testPage) would still need to have the .html extension, otherwise EJS won't find it (unless you tell Express otherwise).

As @ksloan points out in the comments: you do have to be careful what myVar contains. If it contains user-generated content, this may leave your site open for script injection attacks.

A possible solution to prevent this from happening:

<script>
  function htmlDecode(input){
    var e = document.createElement('div');
    e.innerHTML = input;
    return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue;
  }
  var myVar = JSON.parse(htmlDecode("<%= JSON.stringify(myVar) %>"));
</script>
Sign up to request clarification or add additional context in comments.

16 Comments

be careful, because if myVar has any user-generate content in it, your site will be vulnerable to XSS attacks
@robertklep yes, look at this example: jsfiddle.net/k1x230d8 (it just send an alert). This works since JSON.stringify("</script><script>alert('test')</script><script>") returns "</script><script>alert('test')</script><script>" and the <%- %> operators do not escape the result.
@Kito it works iff myVar is passed as a variable from Express to EJS. Your fiddle skips that step (and in fact throws an EJS exception if used as template).
@JoãoPimentelFerreira you can still use EJS to "render" .js files, like this: app.engine('js', require('ejs').renderFile) (and put the .js file in the "views" directory)
@JoãoPimentelFerreira you can change the location of the views directory (or set more than one) using app.set('views', ...)
|
10

if you have more complex objects like an array, you can do this :

<% if (myVar) { %>
   <script>
      myVar = JSON.parse('<%- JSON.stringify(myVar) %>');
   </script>
<% } %>

otherwise, previous solutions you have seen will not work

1 Comment

It was working without this JSON.parse(JSON.stringify()) trick, but VS Code was showing an "Expression expected" error (even with the EJS language support extension). Now it doesn't anymore =D.
10

The main difficulty here is to avoid XSS risks if myVar contains quotes, or </script> for example. To avoid this problem, I propose to use Base64 encoding after JSON.stringify. This would avoid all risks related to quotes or HTML tags since Base64 only contains "safe" characters to put in a quoted string.

The solution I propose:

EJS file:

<script>
  var myVar = <%- passValue(myVar) %>
</script>

which will render into something like (for example here myVar = null):

<script>
  var myVar = JSON.parse(Base64.decode("bnVsbA=="))
</script>

Server-side NodeJS:

function passValue(value) {
  return 'JSON.parse(Base64.decode("' + new Buffer(JSON.stringify(value)).toString('base64') + '"))'
}

Client-side JS (this is an implementation of Base64 decoding that works with Unicode, you can use another if you prefer but be careful if it supports Unicode):

var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(e){var t="";var n,r,i,s,o,u,a;var f=0;e=Base64._utf8_encode(e);while(f<e.length){n=e.charCodeAt(f++);r=e.charCodeAt(f++);i=e.charCodeAt(f++);s=n>>2;o=(n&3)<<4|r>>4;u=(r&15)<<2|i>>6;a=i&63;if(isNaN(r)){u=a=64}else if(isNaN(i)){a=64}t=t+this._keyStr.charAt(s)+this._keyStr.charAt(o)+this._keyStr.charAt(u)+this._keyStr.charAt(a)}return t},decode:function(e){var t="";var n,r,i;var s,o,u,a;var f=0;e=e.replace(/[^A-Za-z0-9\+\/\=]/g,"");while(f<e.length){s=this._keyStr.indexOf(e.charAt(f++));o=this._keyStr.indexOf(e.charAt(f++));u=this._keyStr.indexOf(e.charAt(f++));a=this._keyStr.indexOf(e.charAt(f++));n=s<<2|o>>4;r=(o&15)<<4|u>>2;i=(u&3)<<6|a;t=t+String.fromCharCode(n);if(u!=64){t=t+String.fromCharCode(r)}if(a!=64){t=t+String.fromCharCode(i)}}t=Base64._utf8_decode(t);return t},_utf8_encode:function(e){e=e.replace(/\r\n/g,"\n");var t="";for(var n=0;n<e.length;n++){var r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r)}else if(r>127&&r<2048){t+=String.fromCharCode(r>>6|192);t+=String.fromCharCode(r&63|128)}else{t+=String.fromCharCode(r>>12|224);t+=String.fromCharCode(r>>6&63|128);t+=String.fromCharCode(r&63|128)}}return t},_utf8_decode:function(e){var t="";var n=0;var r=c1=c2=0;while(n<e.length){r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r);n++}else if(r>191&&r<224){c2=e.charCodeAt(n+1);t+=String.fromCharCode((r&31)<<6|c2&63);n+=2}else{c2=e.charCodeAt(n+1);c3=e.charCodeAt(n+2);t+=String.fromCharCode((r&15)<<12|(c2&63)<<6|c3&63);n+=3}}return t}}

Comments

4

Try this:

<script type="text/javascript">
     window.addEventListener('load', function(){
         alert('<%= myVar %>');
     });
</script>

Comments

3

Heres how i made it work, in node js pass the json like this

let j =[];
//sample json
j.push({data:"hi});

res.render('index',{json:j});

now in js function

var json = JSON.parse('<%- JSON.stringify(json) %>');

This worked well for me

Comments

0

Per the documentation here:

Go to the Latest Release, download ./ejs.js or ./ejs.min.js.

Include one of these on your page, and ejs.render(str).

Comments

0

In the accepted solution JSON.parse will fail if myVar has a property with value with unescaped double quote. So better traverseObj and escape each string property.

Here is a function that covers my case:

function traverseObj (obj, callback)
{
    var result = {};
    if ( !isArray(obj) && !isObject(obj) ) {
         return callback(obj);
    }

    for ( var key in obj ) {
        if ( obj.hasOwnProperty(key) ) {
            var value = obj[key];
            if (isMongoId(value)){
                var newValue = callback(value.toString());
                result[key] = newValue;
            }
            else if (isArray ( value) ) {
                var newArr = [];
                for ( var i=0; i < value.length; i++ ) {
                    var arrVal = traverseObj(value[i], callback);
                    newArr.push(arrVal);
                }
                result[key] = newArr;
            }
            else if ( isObject(value) ) {
                result[key] = traverseObj(value, callback);
            }
            else {
                var newValue = callback(value);
                result[key] = newValue;
            }
        }
    }
    return result;
};

Than in ejs you simply has to:

<%
    var encodeValue = function(val) {
        if ( typeof val === 'string' ) {
            return sanitizeXSS(val); //use some library (example npm install xss)
        }
        return val;
    }

    var encodedProduct = ejs_utils.traverseObj(product, encodeValue);
%>

and now you can transport is safely with unescaped syntax

window.product = <%-JSON.stringify(encodedProduct)%>;

Comments

0

I had the similar problem. i got the value

var inJavascript = JSON.parse("<%= myVar %>");

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.