How can I access ejs variables in an external script - javascript

When I pass a variable to my html page with NodeJS I can access it with an inline script as follows:
// passing myVar from the server
router.get('/', (req, res) => {
res.render('index', { myVar: 'test'});
});
// access in index.ejs
<script>
let myVar = <%- JSON.stringify(myVar) %>;
alert(myVar);
</script>
If I try to use an external script here instead of the inline I encounter an error over using the ejs syntax <%- %>. How can I access myVar in an external script?

Code between <%- and %> is server side code.
If you move it out of your EJS template file and into a static JS file, then you can't get at the data. It won't be replaced and you'll send the EJS template to the browser instead of processing it on the server to generate a document.
If you move it out of the EJS template file that generates HTML and into a different EJS template file that generates JavaScript then it will work as normal (except that it will have a different URL with a different end point in your server side code, so you will need to move the server side code which populates the variables you are trying to access).
You have two reasonable options:
Have two script elements.
One to get the data for the page:
<script>let myVar = <%- JSON.stringify(myVar) %>;</script>
And one to contain the JavaScript logic:
<script src="/js/logic.js"></script>
Move the generated data into the page
<div data-myVar="<%- JSON.stringify(myVar).replace(/"/g, """); %>">...</div>
… and then access it through the DOM.

you can put your variable in data-value for get with js or jquery in script e.g :
view ejs side :
<div class='test' data-test-value='<%= JSON.stringify(myVar) %'></div>
js script side :
-if you use jquery:
var $test = $('.test').attr('data-test-value')
alert($test);
-if you use vanillaJs :
var test = document.getElementsByClassName('test');
var testValue = test[0].dataset.testValue;
alert(testValue)
I dont test my vanilla script refer to doc if im wrong on syntax

Related

Why is EJS replacing " with &#34 when sending JSON string to Javascript Variable in html code [duplicate]

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!
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?
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.
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>
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.
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)
}
You can assign backend js to front end ejs by making the backend js as a string.
<script>
var testVar = '<%= backEnd_Var%>';
</script>
This should work
res.render("board", { gameState : game.gameState });
in frontend js
const gameState = '<%- JSON.stringify(gameState) %>'
Well, in this case you can simply use input text to get data. It is easy and tested when you use it in firebase.
<input type="text" id="getID" style="display: none" value="<%=id%>">
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()
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

Can't Render EJS Template on Client

I'm coding an application on express, and I'm using ejs as a view/template engine.
At path /artists, I'm rendering the view artists.ejs which has artists covers. When clicking on a cover, I want an AJAX call to retrieve the corresponding data, place it in my template/view for artist artist.ejs and display this template in my HTML under the cover.
I've seen this related question but it has not solved my use case.
Everything seems clear, but I can't render the data with the template. I would like to compile the template server-side, send it to the client ready to use, and then fill it in when needed with the data received from the AJAX call.
What I've done:
When calling /artists, compile on server-side using ejs.compile(str, opt):
router.get('/artists', function(req, res) {
// Compile artist template
fs.readFile('views/artist.ejs', "utf-8", function(err, template) { // Convert template file to string
artist_template = ejs.compile(template); // Compile template
res.render('artists.ejs', {template: artist_template}); // render page with compiled template
});
I took care of converting the file into String, as ejs compiler only works with String (compared to Jade .compileFile)
Then on client-side, I grab the function:
<script>
var template = <%= template %>
</script>
Then on another script, I retrieve the data with an AJAX call:
$.get('/artists/'+artist_name, function(data) {
var html = template({artist: data});
$('#artist-page').html(html);
}
But when I make the call, I receive:
Uncaught ReferenceError: fn is not defined
When I call the template, fn, I receive:
Uncaught ReferenceError: opts is not defined.
Is the function fn hard-coded? I've read the EJS and Jade documentation but there was little relevant information in regards to my issue.
Do I perhaps need the template on client-side also?
I eventually found a workaround to my question, and I understood with your answer that you could proceed in 2 different ways:
1) What I did: read and save template as a string, then render it client-side with ejs Runtime script.
// In controller.js
var templates = {};
templates.template1 = fs.readFileSync(filePath1, 'utf-8'); // Read template as a string
templates.template2 = fs.readFileSync(filePath2, 'utf-8');
...
res.render('app.ejs', {templates: templates}); // Send templates in view
// In view app.ejs
<script type="text/javascript">
var templates = <%- JSON.stringify(templates) %>; // Get templates object (object of strings)
</script>
<script type="text/javascript" src="/JS/ejs.min.js"></script> <!-- Load ejs RunTime -->
// In site.js - javascript client/public file
$.get('/artists', function(data) {
var html = ejs.render(templates.template1, data); // Render ejs client side with EJS script (template1 corresponds to the artists template)
$('#artists-wrapper').html(html); // Sets HTML
});
Thus, I send all my templates on first page load, and then I render the requested page on the client side. The interest, according to what I've read, is that you only send JSON object (your data) through AJAX calls, and not the entire page, making your request light. Only the first load is heavy with all your templates.
2) What I would like to do according to #RyanZim answer: compiling templates server side into functions, send them, and then call them on the client side : template(data). If I understood well, there is no need of EJS client library in this case, and my templates are no longer strings but functions:
// In controller.js
var templates = {};
templates.template1 = ejs.compile(fs.readFileSync(filePath1, 'utf-8'), {client: true}); // Get template as a function
templates.template2 = ejs.compile(fs.readFileSync(filePath2, 'utf-8'), {client: true});
...
res.render('app.ejs', {templates: templates}); // Send templates in view
However, I can't get them in my view:
<script type="text/javascript">
var templates = <%- JSON.stringify(templates) %>; // Get templates object (object of functions)
</script>
is not working. they are functions on the server before I send them, but I don't know how to recover them. Do you have an idea ?
I tried a workaround, by changing them into String before sending them:
templates.template1 = templates.template1.toString();
Send them and then client side, transform them back in functions:
var template = new Function(templates.template1);
$.get('/artists', function(data) {
var html = template(data);
$('#artists-wrapper').html(html); // Sets HTML
});
But that won't work either.
Do you have an idea what I'm missing here?
And last, do you agree that compiling them server side before using the functions is better in terms of computation than rendering each template client-side?
Thanks for the help, and hope that will help anybody else!
You need to use the client option on the server side when you are compiling for the client. From the docs:
client When true, compiles a function that can be rendered
in the browser without needing to load the EJS Runtime
https://github.com/mde/ejs#options
Your server-side code snippet should be:
// Compile artist template
fs.readFile('views/artist.ejs', "utf-8", function(err, template) {
artist_template = ejs.compile(template, {client: true}); // Use client option
res.render('artists.ejs', {template: artist_template});
});

Can a js script get a variable written in a EJS context/page within the same file

As I wrote in the title, I'd like to get a value from a variable written into a ejs page/file, from a javascript file within the same page
EJS:
<% var test = 101; %>
JS:
<script>
var getTest = test;
</script>
Or what if I'd like to use a function (with parameter) written into a EJS file and use this function in a JS context where the parameter is given to the function from JS context
EJS:
<% function fn(par){ ... } %>
JS:
<script>
var test = 101;
<%>fn(test)<%>
</script>
Edit: this Half considers you are using EJS on server side
1) You can pass an ejs variable value to a Javascript variable
<% var test = 101; %> // variable created by ejs
<script>
var getTest = <%= test %>; //var test is now assigned to getTest which will only work on browsers
console.log(getTest); // successfully prints 101 on browser
</script>
simply create an ejs variable and assign the value inside the script tag to the var getTest
Ex: var getTest = <%= test %>;
2) You can't pass an javascript variable value to a ejs variable
Yes, you cant: if it is on server.
Why:
The EJS template will be rendered on the server before the Javscript is started execution(it will start on browser), so there is no way going back to server and ask for some previous changes on the page which is already sent to the browser.
Edit: this Half considers you are using EJS on Client side
3) if EJS is on client side, and pass EJS variable to javascript
The answer above will still work, but you will require to load the script within the EJS template, not the script loaded before the template rendered(in that case it will of-course no be a valid javascript).
4) if EJS is on client side, and pass javascript variable to EJS
I m sorry I have myself not tried this case, but I really look forward if someone answers this case
The above answer didn't work for me. You can use a div this way:
<div id="mydiv" data-test=<%= test %>></div>
And access the data variable 'test' that you gave it in a script tag:
<script>var test = document.getElementById('mydiv').dataset.test</script>
https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset
Solution with a string
<% var test =myString; %> // variable created by ejs
<script>
var getTest = "<%= test %>"; //var test is now assigned to getTest which will only work on browsers
alert(getTest); // successfully prints the string on browser
</script>
Don't forget the quotes around "<%= test %>"
If test is an object, you can use this:
<script>
let getTest = <%- JSON.stringify(test) %>
</script>
Sadly though, depending on what your code editor is, it may be showing that this syntax is bad and will be underlining it with red to mark it as a syntax error. However, this works perfectly fine when you run it.
Inside your <script>,
you can create a div element:
const div = document.createElement('div');
and give it an innerText value like:
div.innerText = `<%= data_from_your_server_response %>`
If you console.log(div), the data from your server response will be displayed.

How do I embed EJS code in static Javascript?

I am using Nodejs and ExpressJS .
I have an HTML page , which has a Javascript file being referred to .
<script type="text/javascript" src="../javascripts/game.js"></script>
I have not embedded all the Javascript into the HTML page itself because its too big .
Now I need my Javascript ( game.js ) to access some of the variables being passed by the controller . I want to do something like this -
var max_players = parseInt("<%= table.total_players %>");
console.log("<%= table.name %>")
I am passing the table variable while rendering the page .
exports.index = function(req,res){
//code
res.render('mytable', {table: table });
};
But this obviously doesnt work because the JS file is being rendered as a static file .
How do I go about if I need to make these variables accessible to the Javascript ?
I read somewhere that this can be achieved by renaming Game.js to Game.ejs . But where do I put the Game.js file ( so that its rendered properly and dynamically ? )
If there are any other ways to achieve this , please also let me know .
Probably the simplest option would be to output the globals you need from table (or table itself) in another <script> before game.js:
<script>
var max_players = <%- JSON.stringify(table.total_players) %>;
console.log(<%- JSON.stringify(table.name) %>);
/* Alternative:
var table = <%- JSON.stringify(table) %>;
var max_players = table.total_players;
console.log(table.name);
*/
</script>
<script type="text/javascript" src="../javascripts/game.js"></script>
Note the use of <%- ... %> vs. <%= ... %>, which will skip HTML-encoding the output as that can cause syntax errors in.
Using JSON.stringify() here takes advantage of the syntax relation between JSON and JavaScript. The values will be written as JSON data server-side, but parsed as JavaScript literals client-side.
If you want to run game.js itself through EJS, you can move it into your ./views directory, add a route for it, and res.render() it.
Note that you'll need to set the Content-Type as the assumed value will be text/html, which could some browsers will refuse to parse.
// ~/views/game-js.ejs
var max_players = <%- JSON.stringify(table.total_players) %>;
console.log(<%- JSON.stringify(table.name) %>);
// ...
app.get('/javascripts/game.js', function (req, res) {
// code
res.setHeader('Content-Type', 'application/javascript');
res.render('game-js', { table: table });
});
Another option would be to have game.js make a request for table. You can see an example of this in a previous edit of this post.

Accessing EJS variable in Javascript logic

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!
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?
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.
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>
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.
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)
}
You can assign backend js to front end ejs by making the backend js as a string.
<script>
var testVar = '<%= backEnd_Var%>';
</script>
This should work
res.render("board", { gameState : game.gameState });
in frontend js
const gameState = '<%- JSON.stringify(gameState) %>'
Well, in this case you can simply use input text to get data. It is easy and tested when you use it in firebase.
<input type="text" id="getID" style="display: none" value="<%=id%>">
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()
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

Categories