This is bascially the same as this question, except that I am using nunjucks as a template engine.
I am passing a variable to a nunjucks template using express's render method:
res.render('template.html', {myObject:myObject})
I want to access it in my client-side javascript. So far, the only way I have figured out is to put it in an invisible HTML element and pull it into the javascript from there:
<span id='local-variable' style="display:none">{{ myObject.name }}</span>
<script>
var myObjectName = $('#local-variable').text();
</script>
Is there a better method?
Use pipe of dump and safe filters:
<script>
var myObjectName = {{ myObject.name | dump | safe }};
</script>
Related
I am new to node.js and Puppeteer and may not be asking this correctly. I was able to create a web scraper to grab data. What I am wanting to do is send this information I received and push it to a HTML file to load locally on my machine to show the results instead of having it inside my console.
Is there anyway to do this?
Do I need to use another framework?
Any useful tips or resources would be great!
To create a HTML file, you can use your variables and fs.writeFileSync to generate a HTML file with your content.
Example:
const fs = require('fs');
// ... crawling is done, variable are filled
const result1 = 'Some data';
const result2 = 'More crawled data';
const resultHtml = `<!DOCTYPE html>
<html lang="de">
<head><title>Crawling results</title></head>
<body>
<p>Result 1: ${result1}</p>
<p>Result 2: ${result2}</p>
<p>...</p>
</body>
</html>`;
fs.writeFileSync('results.html', resultHtml);
You can then open the results.html file locally in your browser to view the results.
You can use a templating language like handlebars to render the data to an HTML template and write the result to an HTML file.
I cannot find a way to include external .js file to Node ejs template. I want to put logic and data into object in external .js file, include that file to index.ejs template and pull data from it.
I tried by inserting standard way
<script src="sample.js"></script>, and it doesn't work
Then I tried ejs specific keyword <% include partials/sample.js %> and this works only for adding partials (ejs code snippets).
I inserted .js file into static directory which is defined in executable server.js, no results again.
But interestingly, including css file into ejs template classic way works fine, for example
<link href="/assets/styles.css" rel="stylesheet" type="text/css" />
Workaround would be to include external ejs file where I would put logic and data inside <% %> tags, but this is obviously a patch and not a viable solution, because ejs is not a js file. Besides, it doesn't work.
I cannot find any solution on Internet. Any hint?
Thanks
You can't.
Note: You can only pass data from .ejs file to .js file but not the other way. It won't work because .ejs is rendered on the server side while .js runs on the client side.
I am assuming 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>
2) You can't pass a js variable value to a ejs variable
Yes, you can't: if it is on server.
Why:
The EJS template will be rendered on the server before the js 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.
A workaround with Express:
myScripts.js
module.export = {
foo() {},
bar() {}
}
Then in your app.js
var myScripts = require('/path/to/myScripts');
res.render('template', {
utils: myScripts
}); // you forgot a ')' here
then in your template.ejs
// utils will act in global scope for this file
<%
utils.foo();
utils.bar();
%>
I believe you are using it contrary to the intent.
In a controller you can define external scripts and styles you want to include like so
res.render('page-one', {
title: 'Page One',
data: pageData,
libs: ['page-one', 'utils'],
styles: ['page-one']
});
In your static assets for your app you have a js folder and a css folder
|- static/
|- css/
|- fonts/
|- img/
|- js/
favicon.ico
|- templates/
In your js folder you place the file page-one.js and utils.js
In your css folder you place the file page-one.css
In the head section of your html in the ejs template
<!-- styles included on all pages -->
<link rel="stylesheet" href="/css/bootstrap.css">
<!-- styles specific to individual pages -->
<% if (typeof styles !== 'undefined') { %>
<% if (styles.length > 0) { %>
<% for (let style of styles) { %>
<link rel="stylesheet" href="/css/<%= style %>.css">
<% } %>
<% } %>
<% } %>
Typically it is best practice to include scripts at closing body tag so they don't block page render so before the closing body tag in your ejs file
<!-- scripts included on all pages -->
<script src='/js/libs/jquery.min.js' type='text/javascript'></script>
<!-- page specific scripts -->
<% if (typeof libs !== 'undefined') { %>
<% for (let lib of libs) { %>
<script src='/js/<%= lib %>.js' type='text/javascript'></script>
<% } %>
<% } %>
</body>
When your page renders it will render the script and CSS includes automatically
The if block is in case you don't define any includes in the controller.
In your Express application you define your static and external script includes like so
Remember up above we created js and css folders inside a directory named static
// Define static assets
app.use(express.static('static'));
// included on all pages
app.use('/js/libs', express.static(path.join(process.cwd(), 'node_modules/jquery/dist'), { maxAge: 31557600000 }));
Finally, if you absolutely must include JavaScript in your template like rendering JSON data, etc. using special ejs tag <%- %>
<% if (jsonData) { %>
<script id="jsonData">var jsonData=<%- JSON.stringify(jsonData) %>;</script>
<% } %>
I was able to do that by:
serving the Js folder/file in the node app entry file like so:
app.use(express.static(path.join(__dirname, 'views/js')));
Added DOM functions in a index.js file which is in views/js folder.
Added script tag that links to the index.js file before the end body tag of index.ejs file like so:
<script scr="index.js" type="text/javascript"></script>
Note that the src in the script tag does not have "./js/index.js". I actually don't know why it works that way (same with external css stylesheet).
You can achieve it by adding the code below:
app.use(express.static(path.join(__dirname,public)));
You have to add a directory named public (or whatever you are naming) in your project folder. In that folder you can add your external JS file.
NB: the expression path.join is used to make the directory public available/accessible when you call/initiate app from outside the project folder.
My objective is to store some data in a module and then be able to retrieve
that data for display using embedded javascript contained in an HTML document. My code for testing this is shown in the following 3 files:
File 1: The HTML (/var/www/html/modTest.html)
<html>
<head>
<title>Module Test</title>
<% var myModule = require("/var/www/cgi-bin/node_modules/modTest") %>
</head>
<body>
<p>Color: <%= myModule.color %>
</body>
</html>
File 2: The Module (/var/www/cgi-bin/node_modules/modTest.js)
var color = "Blue";
module.exports.color = color;
File 3: The CGI Script (/var/www/cgi-bin)
#!/bin/node
var fs = require("fs");
var ejs = require("ejs")
console.log("Content-type: text/html\n");console.log(ejs.render(fs.readFileSync('/var/www/html/modTest.html','utf8')));
When I load the URL of the cgi script into my browser I get a blank page. There is an error message in the httpd error log compaining that "require" is not defined. Can anyone please tell me why this is and (more importantly) how to fix it? Thanks for any input.
... doug
you need require.js or webpack/browserify to link/pack your javascript together for client side and to load your modules with a require - otherwise you would need to include every javascript file in as a
<script src="...">
tag seperately.
In a Node.js Express application is there a way of minifying all Handlebars templates before they are sent to the renderer?
I considered creating an Express middleware that does the minification on the HTML response body - but I soon realised that this is highly ineffective since the minification would occur on every HTTP request.
There has to be a way of minifying .hbs templates and cache them server side?
You can minify on the fly but for performance reasons I would recommend you to minify the file with an external minifier beforehand and therefore you just do it once. Otherwise you have to minify the html every time the file is called upon.
Therefore, another solution is to use html-minifier from the command line, with the option
--ignore-custom-fragments "/{{[{]?(.*?)[}]?}}/"
This regex will ignore everything between {{ and }} and HTML-minify the rest.
Only add the next flag "--continue-on-parse-error", the following command worked for me:
html-minifier --input-dir [SOURCE_DIR] --output-dir [TARGET_DIR] --file-ext hbs --collapse-whitespace --continue-on-parse-error --remove-comments --minify-css true --minify-js true
This did the trick:
https://github.com/helpers/handlebars-helper-minify
Only tiny issue is that you'd have to manually include the helper in every single template:
From the module's Github page:
{{#minify removeComments="true"}}
{{> header }}
{{/minify}}
{{#minify removeEmptyElements="true"}}
{{> body }}
{{/minify}}
{{#minify removeComments="true"}}
{{> footer }}
{{/minify}}
I use node.js/ejs on the server side and backbone.js on the client side.
Both server side and client side use the same templating style.
So the problem is, if I put template code meant for the client inside a template it still get's parsed on the server side.
If found out that something like this works:
<%- "<%= done ? 'done' : '' %\>" %>
However, IMHO this uglifies the code in a way which makes the whole point of using templates useless.
How would you approach this?
Is there a way to define blocks of code inside EJS-templates which do not get parsed like a {literal}-tag used in other templating languages?
Update: For now I use backbone's _.templateSettings to use different delimiters on the client side.
Update: Here's a similar solution in a JSP context: Underscore.js Templates Within JSP
The way I have dealt with this is to override the opening and closing tags on node so that the 2 instances of ejs are lookgin for different tags.
On node you can pass in options
{open:'<%',close:'%>'}
In my case I use <% and <# for my two versions. Then in node ejs template I have something like this (where name is from backbone and everyauth obviously from node):
<% if(everyauth.loggedIn) %><h1><#= name #></h1><% } %>
I assume the question could be read as following because it was this thread Google provide me at first link:
“How I can escape EJS template code delimiter tag only for limited items?”
tl;dr:
Use <%# %> to break the original parsing code (e.g. <<%# %>%= done ? 'done' : '' %<%# %>> will done the following unparsed code <%= done ? 'done' : '' %>)
Long explanation
Imagine a case I decide to change % by ? using { delimiter: '?' } option (that could be the case here, because we want not to use the same has Backbone.js).
Great, that solves your problem. Imagine now later, for some reason, you use your templating system to generate an XML. This XML will start with <?xml version="1.0" encoding="UTF-8"?>.
You will facing the same issue again. What do? You will change the delimiter again? And after that, you will change again? etc. No, for punctual escaping, what we should is just to be capable to say “Not parse this part of the document as EJS”.
So a trick is to avoid EJS understand it's an EJS delimiter parser. So avoid it (in our current case) parse <? (or <% in an original case).
So by simply adding <?# ?> to break the parsing, you will add nothing (the # item is for EJS comment) and you will avoid parser to understand <<?# ?>?xml version="1.0" encoding="UTF-8"?<?# ?>>. The output will be <?xml version="1.0" encoding="UTF-8"?>
Conclusion
In a punctual necessity to avoid EJS parsing, you can just trick the parser to produce the output you need by using <%# %> as a delimiter tag breaker.
For sure, probably in your case, you can just use the marked answer because you will use the EJS tag in a lot of cases.
Well, the way that I currently approach this is to use require.js with the text plugin; this allows me to include the templates using AJAX during development time and have them all compiled into an optimized/minified single file bundle during deploy time.
Of course, if you don't use require.js for dependency management of the rest of your JS code this doesn't work nearly as well, but I can't stand to do javascript dev without require.js anymore now that I'm used to it anyway.
Alternately, there may be other similar technologies that you could use to solve the same problem.
I use backbone.layout.manager on both the client and server side, because I want my templates to be exactly the same.
The way I solved the template delimiter issue was to render the page on the server side, then inject the raw backbone templates.
With new ejs you can add a custom delimiter at client side :
https://github.com/mde/ejs#custom-delimiters
eg :
Custom delimiters can be applied on a per-template basis, or globally:
var ejs = require('ejs'),
users = ['geddy', 'neil', 'alex'];
// Just one template
ejs.render('<?= users.join(" | "); ?>', {users: users}, {delimiter: '?'});
// => 'geddy | neil | alex'
// Or globally
ejs.delimiter = '$';
ejs.render('<$= users.join(" | "); $>', {users: users});
// => 'geddy | neil | alex'