Create PDF using Node.js, Jade and Express - node.js

I'm trying to create a PDF from one of my views.
This view is rendered using Jade and Express, loads some Javascript, some CSS files, extends some Jade views, and queries a database to populate different graphs. The rendering process takes about 7 seconds because it is not very well optimized.
Additionally, the GET request for the view uses passport and connect-ensure-login to verify that the user is logged in before rendering and passes some variables for Jade to use when rendering. This is the handler code:
var express = require('express');
var router = express.Router();
var moment = require('moment');
router.get('/', require('connect-ensure-login').ensureLoggedIn(), function (req, res, next) {
res.render('summary', {
title: 'Summary',
day: moment().format('D'),
month: moment().locale('es').format('MMMM'),
year: moment().format('YYYY')
});
});
module.exports = router;
This is the Jade file that needs to be converted to PDF:
extends layout
block content
.container-fluid
#banner
.chart#chart1
.chart#chart2
.chart#chart3
script(type='text/javascript', src='/javascripts/summary.js')
And this is the Jade file (layout) that is extended:
doctype html
html
head
meta(charset='utf-8')
meta(http-equiv='X-UA-Compatible', content='IE=edge')
meta(http-equiv='content-type', content='text/html; charset=UTF-8')
meta(content='width=device-width, initial-scale=1', name='viewport')
title=title
link(rel='stylesheet', type='text/css', href='/stylesheets/styles.css')
script(type='text/javascript', src='/assets/jquery-3.1.1/jquery-3.1.1.min.js')
script(type='text/javascript', src='/assets/bootstrap-3.3.7/js/bootstrap.min.js')
body
.page-wrapper
block content
How would I go about loading the page and taking a screenshot and converting it to a PDF or rendering it as a PDF from the Jade file?
I've heard of PhantomJS but I can't find information that is up-to-date.
If anyone could help me out I would be very grateful.

What you can do, is use the jade/pug to generate the .html file with the desired values, save it in the File System (Async way) once this is done, use this library:
https://www.npmjs.com/package/html-pdf
To Read (Async way) the .html file that you saved in the file system and convert it into a pdf, and finally, stream it to the client.
Maybe you can use another library to instead of write to the File System, pipe it directly to the PDF converter, and it will be faster.
If the template will change frequently, this will have to be done for every request, otherwise, you can cache it, and only generate it the first time, then detect if is already there and pipe it to the response.
Hope it helps.

Related

Can I pass data to a handlebars partial?

I'm making a website in Node JS, and want to display something from the database on the website.
When I fetch the data in node, I pass it of to an html page (an hbs file) using express as the second argument in res.render().
However, that html page (hbs file) which I pass the data to, contains a partial, is it possible to pass that same data which was passed to the hbs file, to the partial that is in that hbs file?
Important code for reference:
Passing the data to the hbs file:
app.get('/about',async (req,res)=>{
const names = await Category.find({});
res.render('about',{
categories:names
});
})
Then I'd like to do something like this
{{>header,{{categories}}}} in order to access the categories data in the partial.

How do I convert a folder of static html files to be served by node/express through jade?

I have a folder of static html files files as part of an existing project. I would like to find the simplest way to serve these via node/express using jade. There is a common head/menu/header section at the top that I would like to strip out and put in the jade template, a common section of js includes at the bottom that should also be in the template, and I would like to send a couple variables (e.g. the user) from the server. I would very much appreciate a pattern of how to do this that shows:
the route and controller for the render and how to set up the corresponding static serving with node
the sections of content in the jade files
where the static files should be stored
how jade links together blocks with the same class/id.
Thank you.
You can just read the static files with fs and then pass the data of each file through res.render('page', {contentHTML: data}). In the client page use the != operator to unescape the data and inject it to the view.
For example:
static.html (suppose that the file is in the views folder)
<div>
<p> some text </p>
</div>
Server: (suppose this code is in the routes folder)
router.get('/static', function(req, res){
fs.readFile('./views/static.html', function(err, data){
res.render('page', {responseHtml: data});
});
});
page.jade: (suppose this view is in the views folder)
extends layout
block content
div!= responseHtml
The app structure is this:
-example
...
-routes
index.js (here is the code show above, in the Server section)
users.js
-views
...
page.jade (client code show above)
static.html (content to load in page.jade)
app.js
package.json

node, express, jade: How to process form data

Is there any best practise code pattern how to process HTML form data with express and jade templates? I was wondering, if it would make sense to use a PHP like self calling loop of the form template, say in your router script you have two handler for the same route, one for GET and the other for POST requests. Something like:
exports.getHandler = function(req, res){
res.render('/formhandling/', {mode: "form-filling"});
}
exports.postHandler = function(req, res){
res.render('/formhandling/', {mode: "form-processing"});
}
and the jade template might look like
extends layout
block content
h1 #{title}
if mode == "form-processing"
p Form data processed...
else
form(name="", method="post", action="/formhandling/")
...
Does that make any sense or did I get something completely wrong?
I feel like you could just use jQuery to hide the form after the user submitted, instead of rendering the response again. If you want to do any processing of the form on the server side, you can do that in your app.js.
app.get('/', routes.form);
app.post('/', function(res,req){
/* form processing here
you could also do this with an external route */
});
Edit: Also, see my answer on this question

What is the correct way to use express with a second div that needs data

I have the following layout.jade file:
!!! 5
html
head
body
#left
#leftbody
#center
#centerbody
#container!= body
#right
#rightbody
And I have the following route.js file:
app.get('/',
function(req, res) {
Post.find({}).execFind(
function(err, data) {
res.render('post/index', {
posts: data
});
});
});
I'm currently rendering #centerbody based on links in the #leftbody. I'm wondering how I could use express to populate the #rightbody. Currently I'm populating a ul in #rightbody using an ajax call in document.ready but was wondering if there was another way using express.
I would strongly advise you to have a look at Jade includes, or, even better, Jade template inheritance
This would allow you to keep, for all your pages, the common parts of the page in separated files. And allow easier maintenance. This is definitely the thing to do.

Emulating the 'layout' functionality of Jade while using Mustache

I setup node and express then integrated the mustache.js template by following the instructions on this page:
http://bitdrift.com/post/2376383378/using-mustache-templates-in-express
So far so good, except I'm having a lot of trouble trying to setup mustache.js to have the same functionality as Jade's "layout". I'm basically trying to setup 1 master file to serve as a shell for my other pages similar to extending a template with Django.
Ex. The layout file could have this:
[html]
[title]my title[/title]
[body]{{content}}[/body]
[/html]
Where {{content}} gets replaced with the contents of a file which I would specify somehow in the route for that page.
I just have no idea how to set this up with express because I'm still a huge newbie with it and the way it's setup with Jade is automagical which seems to be specific to Jade only.
With Jade you just need to make a "layout.jade" file and have something like this as your route:
app.get('/', function(req, res) { res.render('home', { title: 'My home page' }); });
Then it magically adds the contents of home.jade into your layout.jade file wherever you specified the body!= body tag.
So yeah, how can I set something like that up with Mustache? If you know the answer please explain it step by step.
You could write a stache renderer plugin for docpad

Resources