Accessing EJS variable in Javascript logic
Asked Answered
T

10

55

I'm working on a Node.js app (it's a game). In this case, I have some code set up such that when a person visits the index and chooses a room, he gets redirected to the proper room.

Right now, it's being done like this with Express v2.5.8:

server.get("/room/:name/:roomId, function (req, res) {
  game = ~databaseLookup~
  res.render("board", { gameState : game.gameState });
}

Over in board.ejs I can access the gameState manner with code like this:

<% if (gameState) { %>
  <h2>I have a game state!</h2>
<% } %>

Is there a way for me to import this into my JavaScript logic? I want to be able to do something like var gs = ~import ejs gameState~ and then be able to do whatever I want with it--access its variables, print it out to console for verification. Eventually, what I want to do with this gameState is to display the board properly, and to do that I'll need to do things like access the positions of the pieces and then display them properly on the screen.

Thanks!

Tuna answered 2/7, 2012 at 8:2 Comment(0)
G
64

You could directly inject the gameState variable into javascript on the page.

<% if (gameState) { %>
     <h2>I have a game state!</h2>
     <script>
        var clientGameState = <%= gameState %>            
     </script>
<% } %>

Another option might be to make an AJAX call back to the server once the page has already loaded, return the gameState JSON, and set clientGameState to the JSON response.

You may also be interested in this: How can I share code between Node.js and the browser?

Gent answered 2/7, 2012 at 17:27 Comment(12)
I've decided to roll with the AJAX call once the page has already loaded. The problem with your code above is that <%= gameState %> evaluates to [object Object] which is not at all what I want. Thanks!Tuna
I use <% include script %> at the bottom of all my pages that has some standard stuff passed to it, like request object, user object, form errors.Embryonic
if the code evaluates to [object Object], I suggest using JSON.stringify(game.gameState) server side. Then the stringified object will be put into the script tag. Since the ejs rendering normally escapes special characters for HTML, you'll want to use var clientGameState = <%- gameState %> within the script tag (instead of <%= ) If you need the gameState for rendering the page as well, you can do a JSON.parse() to loop over it etc.Cumbrous
Following @Cumbrous here's how I did it: res.render('test', { images: JSON.stringify(images) }) on server side, var test = JSON.parse('<%- images %>'); on client side.Beauregard
@Cumbrous How would I use the JSON.stringify method if the object contained user generated content? AKA how would I protect against injection attacks?Earmuff
@KevinWheeler maybe this needs a separate question with an actual use case. In this particular case, all this render function does is generate a Javascript snippet in your HTML, so one could tamper with the resulting values anyway using a browser's developer tools.Cumbrous
Correct. You can not prevent a user from tampering with data on the client, whether it's in the HTML, CSS, or JS. All you're logic should be calculated on the server. For instance if you're applying a discount at checkout for a shopping cart, you should calculate the discount on the server. Doing so on the client would allow the user to have any discount amount they want. If you're talking about gaming, and cheating, it becomes much more difficult. Take aim bots in counter strike for example. very hard to prevent. Requires a separate question.Gent
Its a subtraction sign not an equal sign!Carleton
what about a function variable? It can't be stringifyFran
I believe this maybe insecure, if for instance the contents of gameState contain malicious strings, they could easily break out of the script context. Here is a relevant article covering a parallel scenario in Django's templating language: adamj.eu/tech/2020/02/18/…Assuming
I'm getting an error. It is SyntaxError: Missing catch or finally after try in [path to ejs file] while compiling ejsStroy
@DenizOzger That's a very bad idea, as the ' string literal is not escaped properly. It would be trivial to inject arbitrary JavaScript into that pageThermobarograph
B
32

I had the same problem. I needed to use the data not for just rendering the page, but in my js script. Because the page is just string when rendered, you have to turn the data in a string, then parse it again in js. In my case my data was a JSON array, so:

<script>
  var test = '<%- JSON.stringify(sampleJsonData) %>'; // test is now a valid js object
</script>

Single quotes are there to not be mixed with double-quotes of stringify. Also from ejs docs:

"<%- Outputs the unescaped value into the template"

The same can be done for arrays. Just concat the array then split again.

Bulldog answered 2/12, 2019 at 10:21 Comment(7)
single-quote does not work but template literal worksSkim
I believe this maybe insecure, if for instance the contents of sampleJsonData contain malicious strings, they could easily break out of the script context. Here is a relevant article covering a parallel scenario in Django's templating language: adamj.eu/tech/2020/02/18/…Assuming
I don't understand why would it contain malicious strings? aren't you in control of what goes in the string? the page is generated in server side and sent as is to the browser, and parsed there.Bulldog
Single quotes work fineTopsyturvy
You're neither escaping the string literal (') nor the html (</script>) properly. It's trivial to inject arbitrary JavaScript code here if you control (parts of) sampleJsonData.Thermobarograph
@Thermobarograph it's assumed that you are sending the sampleJsonData from backend yourself. Your point is true if it's coming from a user controllable input.Bulldog
@ArootinAghazaryan Yes, so why not just do it properly? It's very likely that the backend data contains user-controllable values, as that's the most common reason for needing dynamic variable values.Thermobarograph
P
10

In the EJS template:
ex:- testing.ejs

<html>
<!-- content -->
<script>
    // stringify the data passed from router to ejs (within the EJS template only)
    var parsed_data = <%- JSON.stringify(data) %>  
</script>
</html>

In the Server side script:
ex: Router.js

res.render('/testing', {
    data: data // any data to be passed to ejs template
});

In the linked js (or jquery) script file:
ex:- script.js In JavaScript:

console.log(parsed_data)

In JQuery:

$(document).ready(function(){
    console.log(parsed_data)
 });

Note: 1. user - instead of = in <% %> tag
2. you can't declare or use data passed from router to view directly into the linked javascript or jquery script file directly.
3. declare the <% %> in the EJS template only and use it any linked script file.

I'm not sure but I've found it to be the best practice to use passed data from router to view in a script file or script tag.

Pertinent answered 30/5, 2020 at 13:56 Comment(0)
G
9

I feel that the below logic is better and it worked for me.

Assume the variable passed to the ejs page is uid, you can have the contents of the div tag or a h tag with the variable passed. You can access the contents of the div or h tag in the script and assign it to a variable.

code sample below : (in ejs)

<script type="text/javascript">
    $(document).ready(function() {
        var x = $("#uid").html(); 
        alert(x);  // now JS variable 'x' has the uid that's passed from the node backend.
    });
</script>

<h2 style="display:none;" id="uid"><%=uid %></h2>
Geraldine answered 29/9, 2012 at 19:26 Comment(1)
with the above code, you can easily see the entire UID object when you do view source. This is not the right approach even though it worksBeet
B
3

This works for me.

// bar chart data
    var label = '<%- JSON.stringify(bowlers) %>';
    var dataset = '<%- JSON.stringify(data) %>';

    var barData = {
      labels: JSON.parse(label),
      datasets: JSON.parse(dataset)
    }
Breland answered 11/10, 2020 at 13:2 Comment(1)
That will break if the data contains '. https://mcmap.net/q/333218/-accessing-ejs-variable-in-javascript-logic is a better approach.Moersch
I
2

Based on what has been answered, you can do it as follows;

  res.render("board", { gameState : game.gameState });

Next, you would stringify and then parse it as shown;

const gameState = JSON.parse('<%- JSON.stringify(gameState) %>')

This will do the magic better😀

Ima answered 2/3, 2023 at 12:38 Comment(0)
B
1

This should work

  res.render("board", { gameState : game.gameState });

in frontend js

const gameState = '<%- JSON.stringify(gameState) %>'
Butterbur answered 7/4, 2021 at 13:30 Comment(1)
That will break if the data contains '. https://mcmap.net/q/333218/-accessing-ejs-variable-in-javascript-logic is a better approach.Moersch
F
1

You can assign backend js to front end ejs by making the backend js as a string.

<script>
     var testVar = '<%= backEnd_Var%>';
</script>
Fenestella answered 29/7, 2021 at 14:42 Comment(1)
That will break if the data contains '. https://mcmap.net/q/333218/-accessing-ejs-variable-in-javascript-logic is a better approach.Moersch
O
1

I know this was answered a long time ago but thought I would add to it since I ran into a similar issue that required a different solution.

Essentially I was trying to access an EJS variable that was an array of JSON objects through javascript logic like so:

<script>
   // obj is the ejs variable that contains JSON objects from the backend
   var data = '<%= obj %>';
</script>

When I would then try and use forEach() on data I would get errors, which was because '<%= obj %>' provides a string, not an object.

To solve this:

<script>
   var data = <%- obj %>;
</script>

After removing the string wrapping and changing to <%- (so as to not escape html going to the buffer) I could access the object and loop through it using forEach()

Overuse answered 24/11, 2021 at 17:3 Comment(0)
A
0

Suppose you are sending user data from the node server.

app.get("/home",isLoggedIn,(req,res)=>{
    res.locals.pageTitle="Home"
    res.locals.user=req.user
    res.render("home.ejs");
})

And now you can use the 'user' variable in the ejs template. But to use the same value using client-side javascipt. You will have to pass the data to a variable in the tag.

Passing ejs variable to client-side variable:

<script>
    let user= '<%- JSON.stringify(user) %>'; 
</script>



<script>home.js</script>

Now you can access the user variable at home.js

Alexandra answered 13/9, 2022 at 10:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.