node express how to render handlebars html page to file - node.js

I want to convert some html page to pdf via wkhtmltopdf. However, the html page I want to convert to pdf is dynamically generated using handlebars.
So I think one solution maybe to generate the html page via handlebars but to a file (html file). Then, convert that file to pdf using hkhtmltopdf, then allow the user to, somehow, download the pdf.
So, my question is: how can I render the (handlebars) dynamically generated html page to a file?
Thanks and bye ...

Simple example for create file.
var Handlebars = require('handlebars');
var source = "<p>Hello, my name is {{name}}. I am from {{hometown}}. I have " +
"{{kids.length}} kids:</p>" +
"<ul>{{#kids}}<li>{{name}} is {{age}}</li>{{/kids}}</ul>";
var template = Handlebars.compile(source);
var data = { "name": "Alan", "hometown": "Somewhere, TX",
"kids": [{"name": "Jimmy", "age": "12"}, {"name": "Sally", "age": "4"}]};
var result = template(data);
var fs = require('fs');
fs.writeFile("test.html", result, function(err) {
if(err) {
return console.log(err);
}
});

Using express-handlebars, you should use the advanced mode and create an instance of it like in this example.
The proper way would be to create a view file (like you probably already have per you question) and use the express handlebars instance to render it:
// init code
var exphbs = require('express-handlebars');
var hbs = exphbs.create({
defaultLayout: 'your-layout-name',
helpers: require("path-to-your-helpers-if-any"),
});
app.engine('.file-extention-you-use', hbs.engine);
app.set('view engine', '.file-extention-you-use');
// ...then, in the router
hbs.render('full-path-to-view',conext, options).then(function(hbsTemplate){
// hbsTemplate contains the rendered html, do something with it...
});
HTH

Code above from Alex works perfect. However, my confusion was: I was using 'express-handlebars' and not 'handlebars'. Now, what I can understand is Express-Handlebars is an implementation of Handlebars for an Express application, which I´m using. I just didn't find a way to use the 'compile()' method in Express-Handlebars, so I ended up installing Handlebars (standalone) and used it to compile my (html) template and save the result to disk, just as Alex explained above.
In summary:
1) I know Express-Handlebars is Handlebars for Express app.
2) I don't know how to use "compile()" method just from express-handlebars, so I ended up installing Handlebars (from npm) and using it on the server to produce my html file (from template) and save it to disk.
3) Of course I installed and use Express-Handlebars everywhere to serve my pages in my Express app; just installed Handlebars to produce my html (in the server) with "compile()" method and save the result to disk.
Hope this is understandable. Thanks again and bye ...

Related

Is there a way to render a NodeJs Express view to a variable?

Is there a way to render an express view to a variable as opposed to the response stream?
var view = path.join( __dirname, '/../customer-product/views/copysheet.html');
res.render( view, {
data: product
})
I need the html on the server side so that I can be passed to PhantomJs for PDF generation.
There's a pretty good article on it at Strongloop. The upshot is that you would interact directly with the view engine like so:
var templatePath = require.resolve('./copysheet.html');
var templateFn = require('jade').compileFile(templatePath); // or whatever view engine you're usingg
var output = templateFn({data:product});

Load templates from existing file

I'm using Handlebars in my NodeJS application as my templating engine.
I've put all my templates in a views folder like so :
-
- /controllers
- /views
- index.html
- server.js
Here's my code to render the template when the user access a given URL (using express for routing) :
app.get("/", function(req, res){
var template = handlebars.compile("views/index.html");
var data = {"name": "Charles"};
var result = template(data);
res.send(result);
});
I'm trying to render a template from a file, but it's not working. This is what the browser outputs directly when I'm accessing the / URL :
views/index.html
That makes sense, since it's interpreting the given param as a string directly and not as a path to an external template.
How can I load my template file (in this case the one in views/index.html to a variable, so that I can then render the template?
The only examples I found were storing all the templates in a file and loading them via AJAX, but all these examples were from "front-end" handlebars and not when using it with Node.
Is it possible to achieve what I want? I looked at the documentation but it's hard to find good infos for handlebars with NodeJS.
From your description, it sounds like you want handlebars as view engine, with dynamic views. You don't need to do this manually, here is an example (using express-handlebars):
var handlebars = require('express-handlebars');
app.engine('.html', handlebars({layout: false, extname: '.html'}));
app.set('view engine', '.html');
app.get("/:view", function(req, res){
var view = req.params.view;
res.render(view, { "name": "Charles" }); // Whatever data you want
});
With handlebars you have to load the file yourself or you can precompile the files (using grunt/gulp maybe) I feel way more confortable with swig ( http://paularmstrong.github.io/swig/ )
It is very simple to use. And it has also integration with express if you want.
var swig = require('swig');
swig.renderFile('/path/to/template.html', {
pagename: 'awesome people',
authors: ['Paul', 'Jim', 'Jane']
});
In your case
app.get("/", function(req, res){
res.send(swig.renderFile('views/index.html', {"name": "Charles"}));
});

How to serve rendered Jade pages as if they were static HTML pages in Node Express?

Usually you render a Jade page in a route like this:
app.get('/page', function(req, res, next){
res.render('page.jade');
});
But I want to serve all Jade pages (automatically rendered), just like how one would serve static HTML
app.use(express.static('public'))
Is there a way to do something similar for Jade?
"static" means sending existing files unchanged directly from disk to the browser. Jade can be served this way but that is pretty unusual. Usually you want to render jade to HTML on the server which by definition is not "static", it's dynamic. You do it like this:
app.get('/home', function (req, res) {
res.render('home'); // will render home.jade and send the HTML
});
If you want to serve the jade itself for rendering in the browser, just reference it directly in the url when loading it into the browser like:
$.get('/index.jade', function (jade) {
//...
});
https://github.com/runk/connect-jade-static
Usage
Assume the following structure of your project:
/views
/partials
/file.jade
Let's make jade files from /views/partials web accessable:
var jadeStatic = require('connect-jade-static');
app = express();
app.configure(function() {
app.use(jadeStatic({
baseDir: path.join(__dirname, '/views/partials'),
baseUrl: '/partials',
jade: { pretty: true }
}));
});
Now, if you start your web server and request /views/partials/file.html in browser you
should be able see the compiled jade template.
Connect-jade-static is good, but not the perfect solution for me.
To begin with, here are the reasons why I needed jade:
My app is a single page app, there are no HTMLs generated from templates at runtime. Yet, I am using jade to generate HTML files because:
Mixins: lots of repeated / similar code in my HTML is shortened by the use of mixins
Dropdowns: I know, lots of people use ng-repeat to fill the options in a select box. This is a waste of CPU when the list is static, e.g., list of countries. The right thing to do is have the select options filled in within the HTML or partial. But then, a long list of options makes the HTML / jade hard to read. Also, very likely, the list of countries is already available elsewhere, and it doesn’t make sense to duplicate this list.
So, I decided to generate most of my HTML partials using jade at build time. But, this became a pain during development, because of the need to re-build HTMLs when the jade file changes. Yes, I could have used connect-jade-static, but I really don’t want to generate the HTMLs at run time — they are indeed static files.
So, this is what I did:
Added a 'use' before the usual use of express.static
Within this, I check for the timestamps of jade and the corresponding html file
If the jade file is newer, regenerate the html file
Call next() after the regeneration, or immediately, if regeneration is not required.
next() will fall-through to express.static, where the generated HTML will be served
Wrap the ‘use’ around a “if !production” condition, and in the build scripts, generate all the HTML files required.
This way, I can also use all the goodies express.static (like custom headers) provides and still use jade to generate these.
Some code snippets:
var express = require('express');
var fs = require('fs')
var jade = require('jade');
var urlutil = require('url');
var pathutil = require('path');
var countries = require('./countries.js');
var staticDir = 'static'; // really static files like .css and .js
var staticGenDir = 'static.gen'; // generated static files, like .html
var staticSrcDir = 'static.src'; // source for generated static files, .jade
if (process.argv[2] != 'prod') {
app.use(‘/static', function(req, res, next) {
var u = urlutil.parse(req.url);
if (pathutil.extname(u.pathname) == '.html') {
var basename = u.pathname.split('.')[0];
var htmlFile = staticGenDir + basename + '.html';
var jadeFile = staticSrcDir + basename + '.jade';
var hstat = fs.existsSync(htmlFile) ? fs.statSync(htmlFile) : null;
var jstat = fs.existsSync(jadeFile) ? fs.statSync(jadeFile) : null;
if ( jstat && (!hstat || (jstat.mtime.getTime() > hstat.mtime.getTime())) ) {
var out = jade.renderFile(jadeFile, {pretty: true, countries: countries});
fs.writeFile(htmlFile, out, function() {
next();
});
} else {
next();
}
} else {
next();
}
});
}
app.use('/static', express.static(staticDir)); // serve files from really static if exists
app.use('/static', express.static(staticGenDir)); // if not, look in generated static dir
In reality, I have a js file containing not just countries, but various other lists shared between node, javascript and jade.
Hope this helps someone looking for an alternative.

NodeJS, Express - Render EJS view to a download file

I have a NodeJS, Express 3 app that uses EJS templating. I have a route that enables 'preview' of some form fields containing HTML and CSS that are posted. It simply plugs them in and renders my preview.ejs template like this..
app.post('/preview', function(req, res){
var htm = req.body.htm;
var css = req.body.css;
res.render('preview',{_layoutFile:'', htm:htm, css:css});
});
What I'd like to do now is a similar route that forces download of the file instead..
app.post('/download', function(req, res){
var htm = req.body.htm;
var css = req.body.css;
// res.download or res.sendfile here ??
});
I see that there is a res.sendfile() and res.download(), but how can I use these with my EJS 'preview' view template?
P.S. - I'm also using Mikeal's request is this same app. I wonder if there is a way I could use pipe to fs (filesystem) and then force download of the saved file?
See: http://expressjs.com/api.html#res.attachment
res.attachment() sets Content-Disposition: attachment header which tells the browser to treat the response as a download.
app.post('/download', function(req, res){
var htm = req.body.htm;
var css = req.body.css;
res.attachment();
res.render('preview',{_layoutFile:'', htm:htm, css:css});
});

Render Image Stored in Mongo (GridFS) with Node + Jade + Express

I have a small .png file stored in Mongo using GridFS. I would like to display the image in my web browser using Node + Express + Jade. I can retrieve the image fine e.g.:
FileRepository.prototype.getFile = function(callback,id) {
this.gs = new GridStore(this.db,id, 'r');
this.gs.open(callback);
};
but I don't know how to render it using the Jade View Engine. There doesn't seem to be any
information in the documentation.
Can anyone point me in the right direction?
Thanks!
I figured this out (thanks Timothy!). The problem was my understanding of all these technologies and how they fit together. For anyone else who's interested in displaying images from MongoDB GridFS using Node, Express and Jade ...
My Document in MongoDB has a reference to the Image stored in GridFS which is an ObjectId stored as
a string. e.g. MyEntity {ImageId:'4f6d39ab519b481eb4a5cf52'} <-- NB: String representation of ObjectId. The reason I stored it as a string was because storing the ObjectId was giving me a pain
in the Routing as it was rendering as binary and I couldn't figure out how to fix this. (Maybe someone can help here?). Anyway, the solution I have is below:
FileRepository - Retrieve the image from GridFS, I pass in a String Id, which I then convert to
a BSON ObjectId (you can also get the file by file name):
FileRepository.prototype.getFile = function(callback,id) {
var gs = new GridStore(this.db,new ObjectID(id), 'r');
gs.open(function(err,gs){
gs.read(callback);
});
};
Jade Template - Render the HTML Markup:
img(src='/data/#{myentity.ImageId}')
App.JS file - Routing (using Express) I setup the '/data/:imgtag' route for dynamic images:
app.get('/data/:imgtag', function(req, res) {
fileRepository.getFile( function(error,data) {
res.writeHead('200', {'Content-Type': 'image/png'});
res.end(data,'binary');
}, req.params.imgtag );
});
And that did the job. Any questions let me know :)
I'm a little confused about what you're trying to do here, as Jade is a condesed markup language for text output (such as HTML), not binary content.
Since you're using Jade you probably have something like this:
app.get/'images/:imgtag', function(req, res) {
res.render('image', {imgtag: req.params.imgtag);
});
So try this:
app.get/'images/:imgtag', function(req, res) {
filerep.getFile( function(imgdata) {
res.header({'Content_type': 'image/jpeg'})
res.end(imgdata);
}, req.params.imgtag );
});
That will send the raw file as a response to the HTTP request with the correct mime type. If you want to use Jade to deliver a template (such as an image popup) you could use a different route for the popup or even use a data: uri and encode the image data in the page.

Resources