39

I have a pretty large object that I need to pass to a function in a client script. I have tried using JSON.stringify, but have run into a few issues with this approach - mostly performance related. Is it possible to do something like this in ejs?

app.get('/load', function(req, res) {
    var data = {
        layout:'interview/load',
        locals: {
            interview: '',
            data: someLargeObj
        }
    };
    res.render('load', data);
});

And in my client script, I would pass this object to a function like so

<script type="text/javascript">
    load(<%- data %>); // load is a function in a client script
</script>

When I try this I get either

<script type="text/javascript">
    load();
</script>

or

<script type="text/javascript">
    load([Object object]);
</script>
Errol Fitzgerald
  • 2,978
  • 2
  • 26
  • 34
  • `JSON.stringify` is the only way. – Florian Margaine Jun 22 '12 at 07:54
  • @FlorianMargaine, can you please tell why to use 'JSON.stringify()' at client side? as node will pass data as form of object and stringify will just convert it to string instead we need an object right? It's not working without JSON.stringify() – Vishal-L May 13 '18 at 13:28
  • @Vishal-Lia — You should be using `JSON.stringify` **server-side** in the EJS code, not client-side in the ` – Quentin Dec 27 '19 at 00:37
  • @Vishal-Lia did you find out the answer to your question? None of the answer below tells us why we have to `JSON.stringify` when data is passed as an object in an instance. Also, why does `JSON.stringify` convert it into object when it is supposed to do the opposite? – jedu Jun 16 '21 at 15:22

5 Answers5

91

In Node.js:

res.render('mytemplate', {data: myobject});

In EJS:

<script type='text/javascript'>
  var rows =<%-JSON.stringify(data)%>
</script>

SECURITY NOTE : Don't use this to render an object with user-supplied data. It would be possible for someone like Little Bobby Tables to include a substring that breaks the JSON string and starts an executable tag or somesuch. For instance, in Node.js this looks pretty innocent...

var data = {"color": client.favorite_color}

but could result in a client-provided script being executed in user's browsers if they enter a color such as:

"titanium </script><script>alert('pwnd!')</script> oxide"

If you need to include user-provided content, please see https://stackoverflow.com/a/37920555/645715 for a better answer using Base64 encoding

prototype
  • 7,249
  • 15
  • 60
  • 94
  • 4
    Could you tell me the difference between `<%-` and `<%=`? And why no `;` at the end? – gr3g Apr 03 '15 at 15:56
  • 16
    `<%= x %>` interpolates the value of x directly and `<%-x%>` also HTML-escapes it, so characters like `<` and `>` don't get eaten by the HTML parser. – prototype Apr 04 '15 at 00:44
  • 1
    In Javascript terminal semicolons are optional, but probably better to include though. – prototype Apr 04 '15 at 00:45
  • 1
    @prototype awesome bro !!! saved my day !!! ^_^ perfect answer , was trying to HTML escape from a long time... – Partha Roy Apr 20 '17 at 11:59
  • 1 hour frustrated debugging and this was a relief for my pain! Thank you @prototype – Combine Dec 17 '17 at 15:21
  • 1
    @prototype, can you please tell why to use 'JSON.stringify()' at client side? as node will pass data as form of object and stringify will just convert it to string instead we need an object right? It's not working without JSON.stringify() – Vishal-L May 13 '18 at 13:25
  • 2
    @Vishal-Lia Node has the live object, but the html page is text. If you don't stringify, the object will be rendered as "[Object]". Using stringify allows the html page to represent the object as something the browser can parse back into a live object. – prototype May 15 '18 at 21:10
  • So even if it's inside – Vishal-L May 16 '18 at 04:48
  • 1
    Oh man I wish I saw this answer last night. I was about to throw my computer out the window. Thank you. – Ryan Oct 12 '19 at 16:54
  • This does not seem to work when used inside an onclick event handler. Something like: ``. The onclick function is not show in the event listener tab. – zean_7 Feb 20 '22 at 12:19
  • @zean_7 I suspect what is happening is that the stringified data has double quotes, e.g. `{"basket": "red"}` which (if not escaped) can break the syntax when embedded inside double quotes inside the HTML, e.g. ``. Interpolating data within HTML and scripts (and especially user supplied data), gets crazy complicated to escape, and can be a source of security vulnerabilities. – prototype Mar 02 '22 at 02:51
15

That is the expected behavior. Your template engine is trying to create a string from your object which leads to [Object object]. If you really want to pass data like that I think you did the correct thing by stringifying the object.

Pickels
  • 33,902
  • 26
  • 118
  • 178
5

If you are using templating, then it would be much better to get the values in the template, for example whether user is signed in or not. You can get the send local data using

<script>
    window.user = <%- JSON.stringify(user || null) %>
</script>

From the server side code, you are sending user data.

res.render('profile', {
    user: user.loggedin,
    title: "Title of page"
});
Piyush Patel
  • 1,646
  • 1
  • 14
  • 26
1

Think there's a much better way when passing an object to the ejs , you dont have to deal with JSON.stringfy and JSON.parse methods, those are a little bit tricky and confusing. Instead you can use the for in loop to travel the keys of your objects, for example:

if you have an object like such hierarchy

{
    "index": {
        "url": "/",
        "path_to_layout": "views/index.ejs",
        "path_to_data": [
            "data/global.json",
            {
                "data/meta.json": "default"
            }
        ]
    },
    "home": {
        "url": "/home",
        "path_to_layout": "views/home/index.ejs",
        "path_to_data": [
            "data/global.json",
            {
                "data/meta.json": "home"
            }
        ]
    },
    "about": {
        "url": "/about",
        "path_to_layout": "views/default.ejs",
        "path_to_data": [
            "data/global.json",
            {
                "data/meta.json": "about"
            }
        ]
    }
}

On the EJS side you can loop yourObject like this;

<% if ( locals.yourObject) { %>
  <% for(key in yourObject) { %>
    <% if(yourObject.hasOwnProperty(key)) { %>
      <div> <a class="pagelist" href="<%= yourObject[key]['subkey'] %>"><%= key %></a></div>
    <% } %>
  <% } %>
<% } %>

For this example [key] can take 'index','home' and 'about' values and subkey can be any of it's children such as 'url','path_to_layout','path_to_data'

btargac
  • 392
  • 2
  • 10
-2

What you have is a result like this [{'re': 'tg'}]

You actually need to loop it. See javascript while loop https://www.w3schools.com/js/js_loop_while.asp

Then, render it in your front end with ejs... i can't help on that, i use hbs

Mimigirl
  • 1
  • 3