Is it possible to share some code between several pages in Node.js like with PHP without using pug or ejs? - node.js

I want to include a header, a menu and a footer in my web app for most of the pages but I don't want to write the same code in every pages. I know this is possible with PHP, but I'm using Node.js and I can't use jade/pug or EJS. How can I do that ?

To do this, you would create your files that contain your content like the following two file here:
header.js
module.exports = `<nav>
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
</nav>`
footer.js
module.exports = `<footer>
<!-- Your footer code -->
</footer>`
Next within your app, you would listen for a request and then just require the files, and concatenate the data to generate a new string header + body + footer, then you would write that out to the response. I am not sure how you will be getting your body, but here is an example:
main.js
const http = require('http');
const header = require('./header.js');
const footer = require('./footer.js');
const server = http.createServer((req, res) => {
// Get the body somehow
// We will just use a switch here and test `req.url`
let bodyFile = './home.js';
switch(req.url) {
case '/about': bodyFile = './about.js'; break;
case '/contact': bodyFile = './contact.js'; break;
}
const body = require(bodyFile);
res.write(header + body + footer);
res.end();
});
server.listen(8000);

Related

using HTTP.request in ejs render

example in Express I have a route that linked to my ejs middleware.
Code 1 :
app.all("/sample", function(req,res,next){
ejs.renderFile("./sample.ejs", {req,res,next,require,module:require("module")} {}, function(e, dt){
res.send(dt.toString());
});
});
everything fine in the first code. and in sample.ejs (second code) I want to request to some text file in Internet and return to HTML (and should use HTTP module)
Code 2:
<%
var http=require("http");
var url=require("url");
var opt = url.parse("http://website.com/thisfile.txt");
/* it will return "Hello World!" btw */
var dt = ""
var hReq = http.request(opt, function(hRes){
hRes.on("data", function(chunk){
dt+=chunk.toString();
});
});
hReq.end();
%>
<h2>Here is the data is <%= dt %></h2>
and while i try to my browser. it just give me
Code 3:
<h2>Here is the data is </h2>
where I want it gave me
Code 4:
<h2>Here is the data is Hello World!</h2>
How could I get that?
I just want to use HTTP Module or Net Socket Module. and I just want to edit the Code 2. Code 1 is permanently like that.
While EJS can run full JavaScript, you generally want to leave as much as possible out of the template and put more of your logic in your main express request handler.
Since the rendering is done server side anyway, nothing will change other than making it easier to read and test.
You should consider moving the HTTP request made in your EJS template into your app.all('/sample') handler and then just inject the result into your template. In this case that would be the final string collected from the HTTP request. You'll then end up with something like this. (This is untested code).
Also, while it is not required at all, I'd suggest taking a look at something like the request, this makes HTTP requests much easier!
var request = require('request');
app.all("/sample", function(req,res,next){
// Make the HTTP request
request('http://www.website.com/file.txt', function(err, response, body) {
// Render the ejs template
ejs.renderFile("./sample.ejs", {file: body}, function(e, dt) {
// Send the compiled HTML as the response
res.send(dt.toString());
});
});
});

Render HTML with variable data and convert to PDF

I have a html template page that I want to fill in data with via EJS, after which I want to pass this completed page to a PDF creator; the end result being a nice PDF version of my page filled with my data.
For the PDF creator, I'm using the NPM html-pdf do the conversion. Problem is, I don't know of any way I can render the page with my data, save it automatically, then pass the finished page to the PDF creator, since the PDF creator only accepts server paths to saved webpages.
Maybe I'm approaching this the wrong way, but below is what I currently have which admittedly isn't a lot. Any help in the right direction would be appreciated.
var renderPDF = function() {
var pdf = require('html-pdf');
// pulls html page
var html = fs.readFileSync('views/assets/html/render.ejs', 'utf8');
var options = {};
// creates views/test.pdf
pdf.create(html, options).toFile('views/test.pdf', function(err, res) {
if (err) return console.log(err);
console.log(res);
});
};
// this is how I usually render ejs files with data
response.render('assets/html/render.ejs', {myDataOject});
// start the conversion
renderPDF();
Here is the solution.
We read EJS's template file then compile it to PDF format.
index.js
var fs = require('fs');
var ejs = require('ejs');
var pdf = require('html-pdf')
var compiled = ejs.compile(fs.readFileSync(__dirname + '/template.html', 'utf8'));
var html = compiled({ title : 'EJS', text : 'Hello, World!' });
pdf.create(html).toFile('./result.pdf',() => {
console.log('pdf done')
})
template.html
<html>
<head>
<title><%= title %></title>
</head>
<body>
<p><%= text %></p>
</body>

Retrieving HTML from CouchBase into Node.js / Express 4 leaves it unrendered

I'm having a small issue with rendering HTML, stored in CouchBase, fetched by Node.js
In CouchBase I have several small HTML-snippets. They contain text, tags such as <br /> and html entities such as <. They are of course stored as an escaped string in JSON.
So far, so good. However when I pull it out and display on the page, it is rendered "as-is", without being interpreted as HTML.
For example:
[ some content ...]
<p>Lorem is > ipsum<br />And another line</p>
[rest of content ...]
From the controller in Express 4:
var express = require('express');
var router = express.Router();
var couchbase = require('couchbase');
var cluster = new couchbase.Cluster('couchbase://myserver');
var bucket = cluster.openBucket('someBucket', 'somePassword');
var Entities = require('html-entities').XmlEntities;
entities = new Entities();
var utf8 = require('utf8');
/* GET home page. */
router.get('/', function(req, res) {
bucket.get('my:thingie:44', function(err, result) {
if(err) throw err
console.log(result);
var html = utf8.decode(entities.decode(result.value.thingie.html));
// var html = utf8.encode(result.value.thingie.html);
// var html = utf8.decode(result.value.thingie.html);
res.render('index', { title: 'PageTitle', content: html });
});
});
It is then passed to the template (using hogan.js) for rendering.
When looking into this I found that it might have something to do with the encoding of the <'s and <'s that prevent it from being parsed. You can see my converting attempts in the code, where none of the options gave the desired result, i.e. rendering the contents as HTML.
When using utf8.decode(), no difference.
Using utf8.encode(), no difference.
Using entities.decode() it convert < into < as predicted, but it's not rendered even if <div;&gt becomes <div>.
Any ideas?
I found the solution over here: Partials with Node.js + Express + Hogan.js
When putting HTML in a Hogan template, you have to use {{{var}}} instead of {{var}}.
And thus it renders beautifully, as intended :)
Wasn't encoding issues at all ;)

Is there a way to get the HTML of a JADE Template before sending it via response

What I usually do is:
res.render('myJadeTemplate');
but I want to add another transformation to the html before attaching it to the response.
How can I get the rendered HTML then modify it and send it over via res.send()
If you are using Express3. There is an additional (and optional) parameter to res.render() that is a callback that will give you the rendered HTML rather than sending it directly to the client.
res.render('myJadeTemplate', function (err, html) {
// html => rendered HTML from jade template
});
Ok I found a solution:
var jade = require('jade');
var fs = require('fs');
var jadetemplate = jade.compile(fs.readFileSync('code.jade', 'utf8'));
var html = jadetemplate({
params:"{Some parames}"
});
console.log(html);
Thanks for this thread
Node says Jade has no method "renderFile", why?

How to use node modules (like MomentJS) in EJS views?

To use MomentJS in views/custom.ejs, what is the correct way (if any)?
Server side
routes/index etc we can easily use require('moment'); etc and it works fine.
Server Side (EJS views)
views/custome.ejs, something like <% var m = require('moment'); %> doesn't work
I am using ExpressJS with EJS as the template engine.
I found another way of doing this, and I think it has some advantages.
Don't polute your code exporting filters.
Access any method without the need to export them all.
Better ejs usage (no | pipes).
On your controller, or view.js do this:
var moment = require('moment');
exports.index = function(req, res) {
// send moment to your ejs
res.render('index', { moment: moment });
}
Now you can use moment inside your ejs:
<html>
<h1><%= moment().fromNow() %></h1>
</html>
I'm not an Node expert, so if anyone see something bad on doing this, let me know! :)
One more option:
This way you are setting the moment variable to a local available to all scripts in any EJS page on your site.
In your "index.js" (or "app.js") file do this: (after you have set up your 'app' with Express)
var moment = require('moment');
var shortDateFormat = "ddd # h:mmA"; // this is just an example of storing a date format once so you can change it in one place and have it propagate
app.locals.moment = moment; // this makes moment available as a variable in every EJS page
app.locals.shortDateFormat = shortDateFormat;
Then in your EJS file you can refer to moment (and shortDateFormat) as variables like this:
<%= moment(Date()).format(shortDateFormat) %>
Perhaps this is slightly more elegant?
var moment = require('moment');
app.locals.moment = moment;
Use in the view:
<%= moment(myDateValue).fromNow() %>
Now you can simply use moment in your EJS files.
I use moment on the server side with ejs. I wrote an ejs filter function that will return fromNow.
npm install moment
./views/page.ejs
<span class="created_at"><%=: item.created_at | fromNow %></span>
./routes/page.js
var ejs = require('ejs')
, moment = require('moment');
ejs.filters.fromNow = function(date){
return moment(date).fromNow()
}
You can create the function and attach it to the app.locals. and use it in the ejs template on the server side.
In your routes file you do
../routes/page.js
var ejs = require('ejs')
, moment = require('moment');
app.locals.fromNow = function(date){
return moment(date).fromNow();
}
../views/page.ejs
<span class="created_at"><%= fromNow(item.created_at) %></span>
Just remember to have moment added to to your package.json file
How about passing down require like this:
res.render('index', { require: require });
You might need to tweak to maintain the path:
res.render('index', { require: module => require(module /* here you may insert path correction */) });
Obviously this works with Node (backend) only.
The server side (EJS views) which you mentioned above is running on browser and not on your server. You cannot use require because browsers cannot understand it. You need to import the moment.js to use it
<script src="/js/moment.min.js"></script>
also i think it is good idea if you want you can add a middle-ware where you can add anything you want to the theme layer including user,config and moment:
// config, user, moment to the theme layer
app.use(function (req, res, next) {
// grab reference of render
var _render = res.render;
// override logic
res.render = function (view, options, fn) {
// extend config and continue with original render
options = options || {};
options.config = config;
options.moment = moment;
if (req.user && req.user.toJSON) {
options.user = req.user.toJSON();
}
_render.call(this, view, options, fn);
}
next();
});
I wrote a helpers to return moment for using on ejs view and layouts.
./helpers/utils/get-moment.js
const moment = require('moment');
module.exports = {
friendlyName: 'formatMoney',
description: 'format money number.',
inputs: {},
sync: true,
exits: {},
fn: function (inputs, exits) {
return exits.success(moment);
}
};
Then using:
const moment = sails.helpers.utils.getMoment();
As of Node v12.8.3, it seems that you can pass require directly to EJS templates, i.e. this works:
const ejs = require('ejs')
let renderedHTML = ejs.render(`<% const moment = require('moment') %>`, { require })

Resources